alarmdialog.cpp 42.9 KB
Newer Older
Laurent Montel's avatar
Laurent Montel committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
  This file is part of the KDE reminder agent.

  Copyright (c) 2000,2003 Cornelius Schumacher <schumacher@kde.org>
  Copyright (c) 2008-2009 Allen Winter <winter@kde.org>
  Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

  As a special exception, permission is given to link this program
  with any edition of Qt, and distribute the resulting executable,
  without including the source code for Qt in the source distribution.
*/

27
#include <config-korganizer.h>
Laurent Montel's avatar
Laurent Montel committed
28
29
30
31
32
#include "alarmdialog.h"
#include "korganizer_interface.h"
#include "mailclient.h"
#include "koalarmclient_debug.h"

33
34
35
#include "notifications_interface.h" // DBUS-generated
#include "dbusproperties.h" // DBUS-generated

Laurent Montel's avatar
Laurent Montel committed
36
37
38
39
40
41
42
43
#include <CalendarSupport/IncidenceViewer>
#include <CalendarSupport/KCalPrefs>
#include <CalendarSupport/IdentityManager>
#include <CalendarSupport/Utils>

#include <IncidenceEditor/IncidenceDialog>
#include <IncidenceEditor/IncidenceDialogFactory>

44
45
#include <KCalendarCore/Event>
#include <KCalendarCore/Todo>
Laurent Montel's avatar
Laurent Montel committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <KCalUtils/IncidenceFormatter>

#include <KIdentityManagement/Identity>

#include <AkonadiCore/Item>

#include <MailTransport/TransportManager>
#include <QUrl>

#include <KComboBox>
#include <QHBoxLayout>
#include <KLocalizedString>
#include <KMessageBox>
#include <KNotification>
#include <KSharedConfig>
#include <KToolInvocation>
#include <KWindowSystem>
#include <KIconLoader>
#include <QIcon>
65
#include <phonon/mediaobject.h>
Laurent Montel's avatar
Laurent Montel committed
66
67
68
69
70
71
72
73
#include <QLabel>
#include <QKeyEvent>
#include <QSpinBox>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <KConfigGroup>
#include <QDialogButtonBox>
#include <QPushButton>
74
#include <QToolButton>
75
#include <QHeaderView>
Laurent Montel's avatar
Laurent Montel committed
76
77

using namespace KIdentityManagement;
78
using namespace KCalendarCore;
Laurent Montel's avatar
Laurent Montel committed
79
80
using namespace KCalUtils;

81
// fallback defaults
Laurent Montel's avatar
Laurent Montel committed
82
static int defSuspendVal = 5;
83
static int defSuspendUnit = AlarmDialog::SuspendInMinutes;
Laurent Montel's avatar
Laurent Montel committed
84

85
86
87
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
88
89
90
91
class ReminderTreeItem : public QTreeWidgetItem
{
public:
    ReminderTreeItem(const Akonadi::Item &incidence, QTreeWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
92
93
        : QTreeWidgetItem(parent)
        , mIncidence(incidence)
Laurent Montel's avatar
Laurent Montel committed
94
95
    {
    }
Laurent Montel's avatar
Laurent Montel committed
96
97
98
99

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

101
    bool operator<(const QTreeWidgetItem &other) const override;
Laurent Montel's avatar
Laurent Montel committed
102
103
104
105
106

    QString mDisplayText;

    const Akonadi::Item mIncidence;
    QDateTime mRemindAt;
107
    QDateTime mTrigger;
108
    QDateTime mHappening;
Laurent Montel's avatar
Laurent Montel committed
109
    bool mNotified = false;
Laurent Montel's avatar
Laurent Montel committed
110
111
112
113
114
115
116
117
118
119
120
};

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
121
122
    case 1:
    {         // happening datetime
Laurent Montel's avatar
Laurent Montel committed
123
124
125
        const ReminderTreeItem *item = static_cast<const ReminderTreeItem *>(&other);
        return item->mHappening < mHappening;
    }
Laurent Montel's avatar
Laurent Montel committed
126
127
    case 2:
    {         // trigger datetime
Laurent Montel's avatar
Laurent Montel committed
128
129
130
131
        const ReminderTreeItem *item = static_cast<const ReminderTreeItem *>(&other);
        return item->mTrigger < mTrigger;
    }
    default:
Laurent Montel's avatar
Laurent Montel committed
132
        return QTreeWidgetItem::operator <(other);
Laurent Montel's avatar
Laurent Montel committed
133
134
135
136
    }
}

AlarmDialog::AlarmDialog(const Akonadi::ETMCalendar::Ptr &calendar, QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
137
138
139
    : QDialog(parent, Qt::WindowStaysOnTopHint)
    , mCalendar(calendar)
    , mSuspendTimer(this)
Laurent Montel's avatar
Laurent Montel committed
140
141
142
143
144
145
{
    // User1 => Edit...
    // User2 => Dismiss All
    // User3 => Dismiss Selected
    //    Ok => Suspend

146
    if (calendar) {
Laurent Montel's avatar
Laurent Montel committed
147
148
149
        connect(
            calendar.data(), &Akonadi::ETMCalendar::calendarChanged, this,
            &AlarmDialog::slotCalendarChanged);
150
151
        Akonadi::IncidenceChanger *changer = calendar->incidenceChanger();
        changer->setShowDialogsOnError(false);
152
    }
Laurent Montel's avatar
Laurent Montel committed
153
154
155
156
157

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

    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup generalConfig(config, "General");
Laurent Montel's avatar
Laurent Montel committed
158
159
160
161
162
163
164
165
166
167

    const QSize initialSize(424, 187);
    // split up position and size to be compatible with previous version
    // that only stored the position.
    QPoint pos = generalConfig.readEntry("Position", QPoint());
    if (!pos.isNull()) {
        QSize size = generalConfig.readEntry("Size", initialSize);
        mRect = QRect(pos, size);
        setGeometry(mRect);
    }
168

169
    defSuspendVal = generalConfig.readEntry("DefaultSuspendValue", defSuspendVal);
170
    int suspendVal = generalConfig.readEntry("SuspendValue", defSuspendVal);
171

172
    defSuspendUnit = generalConfig.readEntry("DefaultSuspendUnit", defSuspendUnit);
173
174
    SuspendUnit suspendUnit = static_cast<SuspendUnit>(generalConfig.readEntry("SuspendUnit",
                                                                               defSuspendUnit));
Laurent Montel's avatar
Laurent Montel committed
175
176
177
178

    QWidget *topBox = new QWidget(this);
    setWindowTitle(i18nc("@title:window", "Reminders"));
    setWindowIcon(QIcon::fromTheme(QStringLiteral("korgac")));
179
    QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
Laurent Montel's avatar
Minor    
Laurent Montel committed
180
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
181
    mainLayout->addWidget(topBox);
182
    mOkButton = new QToolButton;
Laurent Montel's avatar
Laurent Montel committed
183
184
    mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return);
    mUser1Button = new QPushButton;
Allen Winter's avatar
Allen Winter committed
185
    mUser1Button->setDefault(false);
Laurent Montel's avatar
Laurent Montel committed
186
187
    buttonBox->addButton(mUser1Button, QDialogButtonBox::ActionRole);
    mUser2Button = new QPushButton;
Allen Winter's avatar
Allen Winter committed
188
    mUser2Button->setDefault(false);
Laurent Montel's avatar
Laurent Montel committed
189
190
    buttonBox->addButton(mUser2Button, QDialogButtonBox::ActionRole);
    mUser3Button = new QPushButton;
Allen Winter's avatar
Allen Winter committed
191
    mUser3Button->setDefault(false);
Laurent Montel's avatar
Laurent Montel committed
192
193
194
195
    buttonBox->addButton(mUser3Button, QDialogButtonBox::ActionRole);
    connect(buttonBox, &QDialogButtonBox::accepted, this, &AlarmDialog::accept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &AlarmDialog::reject);
    mainLayout->addWidget(buttonBox);
196
    buttonBox->addButton(mOkButton, QDialogButtonBox::ActionRole);
Laurent Montel's avatar
Laurent Montel committed
197
198
199
200

    mUser3Button->setText(i18nc("@action:button", "Dismiss Reminder"));
    mUser3Button->setToolTip(i18nc("@info:tooltip",
                                   "Dismiss the reminders for the selected incidences"));
201
202
203
204
    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
205
206
207
    mUser2Button->setText(i18nc("@action:button", "Dismiss All"));
    mUser2Button->setToolTip(i18nc("@info:tooltip",
                                   "Dismiss the reminders for all listed incidences"));
208
209
    mUser2Button->setWhatsThis(i18nc("@info:whatsthis",
                                     "Press this button to dismiss all the listed incidences."));
Laurent Montel's avatar
Laurent Montel committed
210
211
212
    mUser1Button->setText(i18nc("@action:button", "Edit..."));
    mUser1Button->setToolTip(i18nc("@info:tooltip",
                                   "Edit the selected incidence"));
213
214
215
    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
216
217
218
219
    mOkButton->setText(i18nc("@action:button", "Suspend"));
    mOkButton->setToolTip(i18nc("@info:tooltip",
                                "Suspend the reminders for the selected incidences "
                                "by the specified interval"));
220
221
222
    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
223

224
    QVBoxLayout *topLayout = new QVBoxLayout(topBox);
Laurent Montel's avatar
Laurent Montel committed
225
226
227
228
229
230
231
232
    // 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);
    QMargins margins(0, 0, 0, 0);
    topLayout->setContentsMargins(margins);
    setContentsMargins(margins);
Laurent Montel's avatar
Laurent Montel committed
233
234
235
236

    QLabel *label = new QLabel(
        i18nc("@label",
              "Reminders: "
Laurent Montel's avatar
Laurent Montel committed
237
              "Clicking on the title toggles details for item"),
Laurent Montel's avatar
Laurent Montel committed
238
        topBox);
239
    topLayout->addWidget(label);
Laurent Montel's avatar
Laurent Montel committed
240

241
    mIncidenceTree = new QTreeWidget(topBox);
Laurent Montel's avatar
Laurent Montel committed
242
243
    mIncidenceTree->setColumnCount(3);
    mIncidenceTree->setSortingEnabled(true);
Laurent Montel's avatar
Laurent Montel committed
244
245
246
247
    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
248
    mIncidenceTree->setHeaderLabels(headerLabels);
249
250
251

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

    header->setSectionResizeMode(1, QHeaderView::ResizeToContents);
Laurent Montel's avatar
Laurent Montel committed
257
258
259
    mIncidenceTree->headerItem()->setToolTip(
        1,
        i18nc("@info:tooltip", "The reminder is set for this date/time"));
260
261
262

    header->setSectionResizeMode(2, QHeaderView::ResizeToContents);
    header->setStretchLastSection(false);
Laurent Montel's avatar
Laurent Montel committed
263
264
265
266
267
268
269
270
271
    mIncidenceTree->headerItem()->setToolTip(
        2,
        i18nc("@info:tooltip", "The date/time the reminder was triggered"));

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

272
    topLayout->addWidget(mIncidenceTree);
Laurent Montel's avatar
Laurent Montel committed
273
274
275

    connect(mIncidenceTree, &QTreeWidget::itemClicked, this, &AlarmDialog::update);
    connect(mIncidenceTree, &QTreeWidget::itemDoubleClicked, this, &AlarmDialog::edit);
276
    connect(mIncidenceTree, &QTreeWidget::itemSelectionChanged, this, &AlarmDialog::updateButtons);
Laurent Montel's avatar
Laurent Montel committed
277
278

    mDetailView = new CalendarSupport::IncidenceViewer(mCalendar.data(), topBox);
Laurent Montel's avatar
Laurent Montel committed
279
280
281
    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
282
    mDetailView->setDefaultMessage(s);
283
    topLayout->addWidget(mDetailView);
Laurent Montel's avatar
Laurent Montel committed
284
    mDetailView->hide();
Laurent Montel's avatar
Laurent Montel committed
285
    mLastItem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
286
287
288

    QWidget *suspendBox = new QWidget(topBox);
    QHBoxLayout *suspendBoxHBoxLayout = new QHBoxLayout(suspendBox);
289
    suspendBoxHBoxLayout->setContentsMargins(0, 0, 0, 0);
290
    topLayout->addWidget(suspendBox);
Laurent Montel's avatar
Laurent Montel committed
291
292
293
294
295
296
297

    QLabel *l = new QLabel(i18nc("@label:spinbox", "Suspend &duration:"), suspendBox);
    suspendBoxHBoxLayout->addWidget(l);

    mSuspendSpin = new QSpinBox(suspendBox);
    suspendBoxHBoxLayout->addWidget(mSuspendSpin);
    mSuspendSpin->setRange(1, 9999);
298
    mSuspendSpin->setValue(suspendVal);    // default suspend duration
Laurent Montel's avatar
Laurent Montel committed
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    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."));

    l->setBuddy(mSuspendSpin);

    mSuspendUnit = new KComboBox(suspendBox);
    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)"));
    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."));
324
    mSuspendUnit->setCurrentIndex(static_cast<int>(suspendUnit));
Laurent Montel's avatar
Laurent Montel committed
325

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
    mSuspendMenu = new QMenu();
    mSuspendMenu->setToolTipsVisible(true);
    mOkButton->setMenu(mSuspendMenu);

    QAction *mResetSuspendAction = new QAction(i18nc("@action:inmenu", "Reset"));
    connect(mResetSuspendAction, &QAction::triggered, this, &AlarmDialog::resetSuspend);
    mResetSuspendAction->setToolTip(i18nc("@info:tooltip",
                                          "Reset the suspend time to the default value"));
    mResetSuspendAction->setWhatsThis(i18nc("@info:whatsthis",
                                            "Reset the suspend time to the default value"));
    mSuspendMenu->addAction(mResetSuspendAction);

    QAction *mSetDefaultSuspendAction = new QAction(i18nc("@action:inmenu", "Set as Default"));
    connect(mSetDefaultSuspendAction, &QAction::triggered, this, &AlarmDialog::setDefaultSuspend);
    mSetDefaultSuspendAction->setToolTip(i18nc("@info:tooltip",
                                               "Set the current suspend time as the new default"));
    mSetDefaultSuspendAction->setWhatsThis(i18nc("@info:whatsthis",
                                                 "Press this button to set the current suspend "
                                                 "time as the new default value"));
    mSuspendMenu->addAction(mSetDefaultSuspendAction);

Laurent Montel's avatar
Laurent Montel committed
347
348
349
350
351
352
353
354
    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;
355
356
357
358
359
360
361
362
363
364

    QDBusConnection dbusConn = QDBusConnection::sessionBus();
    if (dbusConn.interface()->isServiceRegistered(QString::fromLatin1(s_fdo_notifications_service))) {
        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);
    }
Laurent Montel's avatar
Laurent Montel committed
365
366
367
368
369
370
371
372
373
374
}

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

ReminderTreeItem *AlarmDialog::searchByItem(const Akonadi::Item &incidence)
{
Laurent Montel's avatar
Laurent Montel committed
375
    ReminderTreeItem *found = nullptr;
Laurent Montel's avatar
Laurent Montel committed
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
        ReminderTreeItem *item = static_cast<ReminderTreeItem *>(*it);
        if (item->mIncidence == incidence) {
            found = item;
            break;
        }
        ++it;
    }
    return found;
}

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

394
395
396
void AlarmDialog::addIncidence(const Akonadi::Item &incidenceitem,
                               const QDateTime &reminderAt,
                               const QString &displayText)
Laurent Montel's avatar
Laurent Montel committed
397
398
399
400
401
402
403
{
    Incidence::Ptr incidence = CalendarSupport::incidence(incidenceitem);
    ReminderTreeItem *item = searchByItem(incidenceitem);
    if (!item) {
        item = new ReminderTreeItem(incidenceitem, mIncidenceTree);
    }
    item->mNotified = false;
404
    item->mHappening = QDateTime();
Laurent Montel's avatar
Laurent Montel committed
405
    item->mRemindAt = reminderAt;
406
    item->mTrigger = QDateTime::currentDateTime();
Laurent Montel's avatar
Laurent Montel committed
407
408
409
410
    item->mDisplayText = displayText;
    item->setText(0, cleanSummary(incidence->summary()));

    QString displayStr;
411
    const auto dateTime = triggerDateForIncidence(incidence, reminderAt, displayStr);
Laurent Montel's avatar
Laurent Montel committed
412
413
414
415
416
417
418
419
420
421

    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);

422
    item->setText(2, QLocale().toString(item->mTrigger, QLocale::ShortFormat));
Laurent Montel's avatar
Laurent Montel committed
423
424
    QString tip
        = IncidenceFormatter::toolTipStr(
Laurent Montel's avatar
Laurent Montel committed
425
426
              CalendarSupport::displayName(mCalendar.data(), incidenceitem.parentCollection()),
              incidence, item->mRemindAt.date(), true);
Laurent Montel's avatar
Laurent Montel committed
427
428
429
430
431
432
433
434
435
436
437
438
439
    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();
}

440
441
442
443
444
445
446
447
448
449
450
451
void AlarmDialog::resetSuspend()
{
    mSuspendSpin->setValue(defSuspendVal);    // default suspend duration
    mSuspendUnit->setCurrentIndex(defSuspendUnit);
}

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

Laurent Montel's avatar
Laurent Montel committed
452
453
454
455
456
457
458
459
460
void AlarmDialog::slotOk()
{
    suspend();
}

void AlarmDialog::slotUser1()
{
    const ReminderList selection = selectedItems();
    if (!selection.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
461
        ReminderTreeItem *item = selection.first();
Laurent Montel's avatar
Laurent Montel committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
        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
482
483
    const int activeCountNumber = activeCount();
    if (activeCountNumber == 0) {
Laurent Montel's avatar
Laurent Montel committed
484
485
486
487
        accept();
    } else {
        update();
    }
Laurent Montel's avatar
Minor    
Laurent Montel committed
488
    Q_EMIT reminderCount(activeCountNumber);
Laurent Montel's avatar
Laurent Montel committed
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
}

void AlarmDialog::dismissAll()
{
    ReminderList selections;

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
        if (!(*it)->isDisabled()) {   //do not disable suspended reminders
            selections.append(static_cast<ReminderTreeItem *>(*it));
        }
        ++it;
    }
    dismiss(selections);

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

Laurent Montel's avatar
Laurent Montel committed
509
void AlarmDialog::dismiss(const ReminderList &selections)
Laurent Montel's avatar
Laurent Montel committed
510
511
512
{
    QList<Akonadi::Item::Id> ids;
    ids.reserve(selections.count());
Laurent Montel's avatar
Laurent Montel committed
513
514
515
516
    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
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
        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()
{
    ReminderList selection = selectedItems();
Laurent Montel's avatar
Minor    
Laurent Montel committed
533
534
    if (selection.count() == 1) {
        Incidence::Ptr incidence = CalendarSupport::incidence(selection.first()->mIncidence);
Laurent Montel's avatar
Laurent Montel committed
535
536
        if (!mCalendar->hasRight(selection.first()->mIncidence,
                                 Akonadi::Collection::CanChangeItem)) {
Laurent Montel's avatar
Minor    
Laurent Montel committed
537
            KMessageBox::sorry(
Laurent Montel's avatar
Laurent Montel committed
538
539
                this,
                i18nc("@info",
540
                      "\"%1\" is a read-only incidence so modifications are not possible.",
Laurent Montel's avatar
Laurent Montel committed
541
                      cleanSummary(incidence->summary())));
Laurent Montel's avatar
Minor    
Laurent Montel committed
542
543
            return;
        }
Laurent Montel's avatar
Laurent Montel committed
544

545
546
547
548
549
550
551
552
        if (!openIncidenceEditorNG(selection.first()->mIncidence)) {
            KMessageBox::error(
                this,
                i18nc("@info",
                      "An internal error occurred attempting to modify \"%1\". Unsupported type.",
                      cleanSummary(incidence->summary())));
            qCWarning(KOALARMCLIENT_LOG) << "Attempting to edit an unsupported incidence type.";
        }
Laurent Montel's avatar
Minor    
Laurent Montel committed
553
    }
Laurent Montel's avatar
Laurent Montel committed
554
555
556
557
558
559
560
561
562
563
}

void AlarmDialog::suspend()
{
    if (!isVisible()) {   //do nothing if the dialog is hidden
        return;
    }

    int unit = 1;
    switch (mSuspendUnit->currentIndex()) {
564
    case SuspendInWeeks:
Laurent Montel's avatar
Laurent Montel committed
565
        unit *= 7;
Laurent Montel's avatar
Laurent Montel committed
566
        Q_FALLTHROUGH();
567
    case SuspendInDays:
Laurent Montel's avatar
Laurent Montel committed
568
        unit *= 24;
Laurent Montel's avatar
Laurent Montel committed
569
        Q_FALLTHROUGH();
570
    case SuspendInHours:
Laurent Montel's avatar
Laurent Montel committed
571
        unit *= 60;
Laurent Montel's avatar
Laurent Montel committed
572
        Q_FALLTHROUGH();
573
    case SuspendInMinutes:
Laurent Montel's avatar
Laurent Montel committed
574
        unit *= 60;
Laurent Montel's avatar
Laurent Montel committed
575
        Q_FALLTHROUGH();
Laurent Montel's avatar
Laurent Montel committed
576
577
578
579
    default:
        break;
    }

Laurent Montel's avatar
Laurent Montel committed
580
    ReminderTreeItem *selitem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
581
582
583
584
585
586
587
    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
        if ((*it)->isSelected() && !(*it)->isDisabled()) {   //suspend selected, non-suspended reminders
            (*it)->setSelected(false);
            (*it)->setDisabled(true);
            ReminderTreeItem *item = static_cast<ReminderTreeItem *>(*it);
            item->mRemindAt = QDateTime::currentDateTime().addSecs(unit * mSuspendSpin->value());
588
            item->mHappening = item->mRemindAt;
Laurent Montel's avatar
Laurent Montel committed
589
            item->mNotified = false;
590
            (*it)->setText(1, QLocale().toString(item->mHappening, QLocale::ShortFormat));
Laurent Montel's avatar
Laurent Montel committed
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
            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) {
        ReminderTreeItem *item = static_cast<ReminderTreeItem *>(*it);
        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);
    }
}

638
639
640
641
642
643
644
645
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);
646
    KWindowSystem::unminimizeWindow(winId());
647
648
649
    KWindowSystem::setState(winId(), NET::KeepAbove | NET::DemandsAttention);
}

Laurent Montel's avatar
Laurent Montel committed
650
651
652
653
654
655
656
657
658
659
660
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) {
        ReminderTreeItem *item = static_cast<ReminderTreeItem *>(*it);
661
        if (!item->mNotified && !item->isDisabled()) {
Laurent Montel's avatar
Laurent Montel committed
662
663
664
665
666
667
            (*it)->setSelected(true);
            break;
        }
        ++it;
    }

668
669
    mUser2Button->setVisible(mIncidenceTree->topLevelItemCount() > 1);

Laurent Montel's avatar
Laurent Montel committed
670
671
672
673
674
    // reset the default suspend time
// Allen: commented-out the following lines on 17 Sept 2013
//  mSuspendSpin->setValue( defSuspendVal );
//  mSuspendUnit->setCurrentIndex( defSuspendUnit );

Laurent Montel's avatar
Laurent Montel committed
675
676
677
    // 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
678
    }
Laurent Montel's avatar
Laurent Montel committed
679
    QDialog::show();
680
681
682
683
684
    if (grabFocus()) {
        KWindowSystem::activateWindow(winId());
    } else {
        KWindowSystem::raiseWindow(winId());
    }
Laurent Montel's avatar
Laurent Montel committed
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734

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

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

    // first, select all non-suspended reminders
    while (*it) {
        if (!(*it)->isDisabled()) {   //do not suspend suspended reminders
            (*it)->setSelected(true);
        }
        ++it;
    }

    //suspend all selected reminders
    suspend();
}

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

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
        ReminderTreeItem *item = static_cast<ReminderTreeItem *>(*it);
        ++it;
        if (item->isDisabled() || item->mNotified) {
            //skip suspended reminders or reminders that have been notified
            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
735
736
737
                if (program.contains(QLatin1Char(' '))
                    && !(program.startsWith(QLatin1Char('\"'))
                         && program.endsWith(QLatin1Char('\"')))) {
Laurent Montel's avatar
Laurent Montel committed
738
739
740
                    program = QLatin1Char('\"') + program + QLatin1Char('\"');
                }

741
742
                //TODO move alarm->programArguments() as argument of QProcess API ?
                QProcess::startDetached(program + QLatin1Char(' ') + alarm->programArguments(), QStringList());
Laurent Montel's avatar
Laurent Montel committed
743
744
            } else if (alarm->type() == Alarm::Audio) {
                beeped = true;
745
746
747
                Phonon::MediaObject *player
                    = Phonon::createPlayer(Phonon::NotificationCategory,
                                           QUrl::fromLocalFile(alarm->audioFile()));
Laurent Montel's avatar
Laurent Montel committed
748
                player->setParent(this);
749
750
                connect(player, &Phonon::MediaObject::finished, player,
                        &Phonon::MediaObject::deleteLater);
Laurent Montel's avatar
Laurent Montel committed
751
752
753
754
755
756
757
758
759
760
761
                player->play();
            } else if (alarm->type() == Alarm::Email) {
                QString from = CalendarSupport::KCalPrefs::instance()->email();
                Identity id = mIdentityManager->identityForAddress(from);
                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
762
                    Person::List::ConstIterator end(addresses.constEnd());
Laurent Montel's avatar
Laurent Montel committed
763
                    for (Person::List::ConstIterator it = addresses.constBegin();
Laurent Montel's avatar
Laurent Montel committed
764
                         it != end; ++it) {
765
                        add << (*it).fullName();
Laurent Montel's avatar
Laurent Montel committed
766
                    }
767
                    to = add.join(QLatin1String(", "));
Laurent Montel's avatar
Laurent Montel committed
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
                }

                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());
                }

Volker Krause's avatar
Volker Krause committed
785
                QString body = IncidenceFormatter::mailBodyStr(parent.staticCast<IncidenceBase>());
Laurent Montel's avatar
Laurent Montel committed
786
787
788
789
790
                if (!alarm->mailText().isEmpty()) {
                    body += QLatin1Char('\n') + alarm->mailText();
                }
                //TODO: support attachments
                KOrg::MailClient mailer;
791
792
793
794
                const bool sendStatus =
                    mailer.send(id, from, to, QString(), subject, body, true, false, QString(),
                                MailTransport::TransportManager::self()->defaultTransportName());
                if (!sendStatus) {
795
796
                    KNotification::event(QStringLiteral("mailremindersent"),
                        QString(),
797
798
799
                        i18nc("@info email subject, error message",
                              "<warning>Failed to send the Email reminder for %1. %2</warning>",
                              subject, mailer.errorMsg()),
800
801
802
803
804
                        QStringLiteral("korgac"),
                        nullptr,
                        KNotification::CloseOnTimeout,
                        QStringLiteral("korgac"));
                }
Laurent Montel's avatar
Laurent Montel committed
805
806
807
808
809
810
811
812
813
814
815
            }
        }
    }

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

void AlarmDialog::wakeUp()
{
816
817
818
819
820
821
822
823
824
825
826
827
828
    // 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))) {
        OrgFreedesktopNotificationsInterface iface(
                QString::fromLatin1(s_fdo_notifications_service),
                QString::fromLatin1(s_fdo_notifications_path),
                dbusConn);
        if (iface.inhibited()) {
            return;
        }
    }

Laurent Montel's avatar
Laurent Montel committed
829
830
    bool activeReminders = false;
    QTreeWidgetItemIterator it(mIncidenceTree);
Laurent Montel's avatar
Laurent Montel committed
831
    QTreeWidgetItem *firstItem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
    while (*it) {
        if (!firstItem) {
            firstItem = *it;
        }
        ReminderTreeItem *item = static_cast<ReminderTreeItem *>(*it);
        Incidence::Ptr incidence = CalendarSupport::incidence(item->mIncidence);

        if (item->mRemindAt <= QDateTime::currentDateTime()) {
            if (item->isDisabled()) {   //do not wakeup non-suspended reminders
                item->setDisabled(false);
                item->setSelected(false);
            }
            activeReminders = true;
        } else {
            item->setDisabled(true);
        }

        ++it;
    }

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

860
861
862
void AlarmDialog::slotDBusNotificationsPropertiesChanged(const QString &interface,
                                                         const QVariantMap &changedProperties,
                                                         const QStringList &invalidatedProperties)
863
864
865
866
867
868
869
870
871
872
873
874
875
{
    Q_UNUSED(interface); // always "org.freedesktop.Notifications"
    Q_UNUSED(invalidatedProperties);
    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
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
void AlarmDialog::slotSave()
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup generalConfig(config, "General");
    int numReminders = 0;

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
        ReminderTreeItem *item = static_cast<ReminderTreeItem *>(*it);
        KConfigGroup incidenceConfig(config,
                                     QStringLiteral("Incidence-%1").arg(numReminders + 1));

        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
896
897
898
    mRect = geometry();
    generalConfig.writeEntry("Position", mRect.topLeft());
    generalConfig.writeEntry("Size", mRect.size());
899
900
    generalConfig.writeEntry("DefaultSuspendValue", defSuspendVal);
    generalConfig.writeEntry("DefaultSuspendUnit", defSuspendUnit);
901
902
903
    generalConfig.writeEntry("SuspendValue", mSuspendSpin->value());
    generalConfig.writeEntry("SuspendUnit", mSuspendUnit->currentIndex());

Laurent Montel's avatar
Laurent Montel committed
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
    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) {
        if (!(*it)->isDisabled()) {   //suspended reminders are non-active
            ++count;
        }
        ++it;
    }
    qCDebug(KOALARMCLIENT_LOG) << "computed " << count << " active reminders";
    return count;
}

void AlarmDialog::closeEvent(QCloseEvent *)
{
Laurent Montel's avatar
Laurent Montel committed
937
    // note, this is on application close (not window hide)
Laurent Montel's avatar
Laurent Montel committed
938
939
940
941
    slotSave();
    accept();
}

Laurent Montel's avatar
Laurent Montel committed
942
943
944
945
946
947
void AlarmDialog::reject()
{
    slotSave();
    QDialog::reject();
}

Laurent Montel's avatar
Laurent Montel committed
948
949
950
951
952
953
954
955
956
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
957
        ReminderTreeItem *item = selection.first();
958
        if (mCalendar) {
Laurent Montel's avatar
Laurent Montel committed
959
960
            mUser1Button->setEnabled(mCalendar->hasRight(item->mIncidence,
                                                         Akonadi::Collection::CanChangeItem));
961
        }
Laurent Montel's avatar
Laurent Montel committed
962
963
964
    } else {
        mUser1Button->setEnabled(false);
    }
Allen Winter's avatar
Allen Winter committed
965
966
967
968
    if (enabled) {
       mIncidenceTree->setFocus();
       mIncidenceTree->setCurrentItem(selection.first());
    }
Laurent Montel's avatar
Laurent Montel committed
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
}

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;
    }

    ReminderTreeItem *reminderItem = dynamic_cast<ReminderTreeItem *>(item);

    if (!reminderItem) {
        mDetailView->setIncidence(Akonadi::Item());
    } else {
        if (!reminderItem->mDisplayText.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
1004
1005
            const QString txt
                = QLatin1String("<qt><p><b>") + reminderItem->mDisplayText + QLatin1String("</b></p></qt>");
Laurent Montel's avatar
Laurent Montel committed
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
            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
1020
        ReminderTreeItem *item = selection.first();
1021
1022
        mUser1Button->setEnabled(
            (mCalendar->hasRight(item->mIncidence, Akonadi::Collection::CanChangeItem)) && (selection.count() == 1));
Laurent Montel's avatar
Laurent Montel committed
1023
1024
1025
1026
1027
1028
1029
        toggleDetails(item);
    }
}

void AlarmDialog::accept()
{
    if (activeCount() == 0) {
Laurent Montel's avatar
Laurent Montel committed
1030
        slotSave();
Laurent Montel's avatar
Laurent Montel committed
1031
1032
1033
1034
1035
        hide();
    }
}

/** static */
1036
1037
1038
QDateTime AlarmDialog::triggerDateForIncidence(const Incidence::Ptr &incidence,
                                               const QDateTime &reminderAt,
                                               QString &displayStr)
Laurent Montel's avatar
Laurent Montel committed
1039
{
1040
    QDateTime result;
Laurent Montel's avatar
Laurent Montel committed
1041
1042
1043
1044
1045
1046

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

    if (incidence->recurs()) {
1047
        result = incidence->recurrence()->getNextDateTime(reminderAt).toLocalTime();
Laurent Montel's avatar
Laurent Montel committed
1048
1049
1050
    }

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

1054
    if (result.isValid()) {
1055
        displayStr = QLocale().toString(result, QLocale::ShortFormat);
1056
    }
Laurent Montel's avatar
Laurent Montel committed
1057
1058
1059
1060
1061
    return result;
}

void AlarmDialog::slotCalendarChanged()
{
1062
    KCalendarCore::Incidence::List incidences = mCalendar->incidences();
Laurent Montel's avatar
Laurent Montel committed
1063
1064
    const Akonadi::Item::List items = mCalendar->itemList(incidences);
    Akonadi::Item::List::ConstIterator end(items.constEnd());
Laurent Montel's avatar
Laurent Montel committed
1065
    for (Akonadi::Item::List::ConstIterator it = items.constBegin();
Laurent Montel's avatar
Laurent Montel committed
1066
         it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
1067
1068
1069
1070
1071
1072
1073
1074
        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()) {
1075
                const auto dateTime = triggerDateForIncidence(incidence, item->mRemindAt, displayStr);
Laurent Montel's avatar
Laurent Montel committed
1076
1077
                const QString summary = cleanSummary(incidence->summary());

1078
                if (displayStr != item->text(1) || summary != item->text(0) || item->mHappening != dateTime) {
Laurent Montel's avatar
Laurent Montel committed
1079
1080
                    item->setText(1, displayStr);
                    item->setText(0, summary);
Laurent Montel's avatar
Laurent Montel committed
1081
                    item->mHappening = dateTime;
Laurent Montel's avatar
Laurent Montel committed
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
                }
            }
        }
    }
}

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
1102
    if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral(
Laurent Montel's avatar
Laurent Montel committed
1103
                                                                            "org.kde.korganizer"))) {
Laurent Montel's avatar
Laurent Montel committed
1104
1105
        if (KToolInvocation::startServiceByDesktopName(QStringLiteral("org.kde.korganizer"),
                                                       QString())) {
Laurent Montel's avatar
Laurent Montel committed
1106
1107
1108
1109
1110
1111
1112
1113
            KMessageBox::error(
                this,
                i18nc("@info",
                      "Could not start KOrganizer so editing is not possible."));
            return false;
        }
    }
    org::kde::korganizer::Korganizer korganizer(
Laurent Montel's avatar
Laurent Montel committed
1114
1115
        QStringLiteral("org.kde.korganizer"), QStringLiteral(
            "/Korganizer"), QDBusConnection::sessionBus());
Laurent Montel's avatar
Laurent Montel committed
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126

    qCDebug(KOALARMCLIENT_LOG) << "editing incidence " << incidence->summary();
    if (!korganizer.editIncidence(incidence->uid())) {
        KMessageBox::error(
            this,
            i18nc("@info",
                  "An internal KOrganizer error occurred attempting to modify \"%1\"",
                  cleanSummary(incidence->summary())));
    }

    // get desktop # where korganizer (or kontact) runs
Laurent Montel's avatar
Laurent Montel committed
1127
1128
1129
1130
    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
1131
1132
1133
1134
1135
1136
1137
1138
    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) {
1139
            KWindowSystem::minimizeWindow(winId());
Laurent Montel's avatar
Laurent Montel committed
1140
1141
1142
1143
1144
1145
1146
        } 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
1147
    QDBusReply<int> reply = korganizerObj.call(QStringLiteral("winId"));
Laurent Montel's avatar
Laurent Montel committed
1148
    if (reply.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
1149
        int window = reply;
1150
        KWindowSystem::minimizeWindow(winId());
Laurent Montel's avatar
Laurent Montel committed
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
        KWindowSystem::allowExternalProcessWindowActivation();
        KWindowSystem::activateWindow(reinterpret_cast<WId>(window));
    }
#else
    // TODO (mac)
#endif
    return true;
}

bool AlarmDialog::openIncidenceEditorNG(const Akonadi::Item &item)
{
    Incidence::Ptr incidence = CalendarSupport::incidence(item);
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
    IncidenceEditorNG::IncidenceDialog *dialog =
        IncidenceEditorNG::IncidenceDialogFactory::create(
            false, /*doesn't need initial saving*/
            incidence->type(), nullptr, this);
    if (!dialog) {
        return false;
    } else {
        dialog->load(item);
        return true;
    }
Laurent Montel's avatar
Laurent Montel committed
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
}

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());
1189
        const QUrl akonadiUrl(incGroup.readEntry("AkonadiUrl"));
Laurent Montel's avatar
Laurent Montel committed
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
        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);

    //Write everything except those which have an uid we don't want
    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();
}
1216
1217
1218
1219
1220
1221
1222

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