2 This file is part of the KDE reminder agent.
4 Copyright (c) 2000,2003 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (c) 2008-2009 Allen Winter <winter@kde.org>
6 Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 As a special exception, permission is given to link this program
23 with any edition of Qt, and distribute the resulting executable,
24 without including the source code for Qt in the source distribution.
27 #include "config-kdepim.h"
28 #include "alarmdialog.h"
29 #include "korganizer_interface.h"
30 #include "mailclient.h"
31 #include "koalarmclient_debug.h"
33 #include <CalendarSupport/IncidenceViewer>
34 #include <CalendarSupport/KCalPrefs>
35 #include <CalendarSupport/IdentityManager>
36 #include <CalendarSupport/Utils>
38 #include <IncidenceEditor/IncidenceDialog>
39 #include <IncidenceEditor/IncidenceDialogFactory>
41 #include <KCalCore/Event>
42 #include <KCalCore/Todo>
43 #include <KCalUtils/IncidenceFormatter>
45 #include <KIdentityManagement/Identity>
46 #include <KIdentityManagement/IdentityManager>
48 #include <AkonadiCore/Item>
50 #include <MailTransport/TransportManager>
55 #include <QHBoxLayout>
56 #include <KLocalizedString>
57 #include <KMessageBox>
58 #include <KNotification>
59 #include <KSharedConfig>
60 #include <KSystemTimeZone>
61 #include <KToolInvocation>
62 #include <KWindowSystem>
63 #include <KIconLoader>
65 #include <phonon/mediaobject.h>
69 #include <QTreeWidget>
70 #include <QVBoxLayout>
71 #include <KConfigGroup>
72 #include <QDialogButtonBox>
73 #include <QPushButton>
75 using namespace KIdentityManagement
;
76 using namespace KCalCore
;
77 using namespace KCalUtils
;
79 static int defSuspendVal
= 5;
80 static int defSuspendUnit
= 0; // 0=>minutes, 1=>hours, 2=>days, 3=>weeks
82 class ReminderTree
: public QTreeWidget
85 ReminderTree(QWidget
*parent
) : QTreeWidget(parent
)
91 class ReminderTreeItem
: public QTreeWidgetItem
94 ReminderTreeItem(const Akonadi::Item
&incidence
, QTreeWidget
*parent
)
95 : QTreeWidgetItem(parent
), mIncidence(incidence
), mNotified(false)
98 bool operator<(const QTreeWidgetItem
&other
) const Q_DECL_OVERRIDE
;
100 QString mDisplayText
;
102 const Akonadi::Item mIncidence
;
105 KDateTime mHappening
;
115 bool ReminderTreeItem::operator<(const QTreeWidgetItem
&other
) const
117 switch (treeWidget()->sortColumn()) {
118 case 1: { // happening datetime
119 const ReminderTreeItem
*item
= static_cast<const ReminderTreeItem
*>(&other
);
120 return item
->mHappening
< mHappening
;
122 case 2: { // trigger datetime
123 const ReminderTreeItem
*item
= static_cast<const ReminderTreeItem
*>(&other
);
124 return item
->mTrigger
< mTrigger
;
127 return QTreeWidgetItem::operator < (other
);
131 AlarmDialog::AlarmDialog(const Akonadi::ETMCalendar::Ptr
&calendar
, QWidget
*parent
)
132 : QDialog(parent
, Qt::WindowStaysOnTopHint
),
133 mCalendar(calendar
), mSuspendTimer(this)
136 // User2 => Dismiss All
137 // User3 => Dismiss Selected
140 connect(calendar
.data(), &Akonadi::ETMCalendar::calendarChanged
, this, &AlarmDialog::slotCalendarChanged
);
142 KIconLoader::global()->addAppDir(QStringLiteral("korgac"));
144 KSharedConfig::Ptr config
= KSharedConfig::openConfig();
145 KConfigGroup
generalConfig(config
, "General");
146 QPoint pos
= generalConfig
.readEntry("Position", QPoint(0, 0));
148 QWidget
*topBox
= new QWidget(this);
153 setWindowTitle(i18nc("@title:window", "Reminders"));
154 setWindowIcon(QIcon::fromTheme(QStringLiteral("korgac")));
155 QDialogButtonBox
*buttonBox
= new QDialogButtonBox(QDialogButtonBox::Ok
);
156 QVBoxLayout
*mainLayout
= new QVBoxLayout
;
157 setLayout(mainLayout
);
158 mainLayout
->addWidget(topBox
);
159 mOkButton
= buttonBox
->button(QDialogButtonBox::Ok
);
160 mOkButton
->setShortcut(Qt::CTRL
| Qt::Key_Return
);
161 mUser1Button
= new QPushButton
;
162 buttonBox
->addButton(mUser1Button
, QDialogButtonBox::ActionRole
);
163 mUser2Button
= new QPushButton
;
164 buttonBox
->addButton(mUser2Button
, QDialogButtonBox::ActionRole
);
165 mUser3Button
= new QPushButton
;
166 buttonBox
->addButton(mUser3Button
, QDialogButtonBox::ActionRole
);
167 connect(buttonBox
, &QDialogButtonBox::accepted
, this, &AlarmDialog::accept
);
168 connect(buttonBox
, &QDialogButtonBox::rejected
, this, &AlarmDialog::reject
);
169 mainLayout
->addWidget(buttonBox
);
171 mUser3Button
->setText(i18nc("@action:button", "Dismiss Reminder"));
172 mUser3Button
->setToolTip(i18nc("@info:tooltip",
173 "Dismiss the reminders for the selected incidences"));
174 mUser2Button
->setText(i18nc("@action:button", "Dismiss All"));
175 mUser2Button
->setToolTip(i18nc("@info:tooltip",
176 "Dismiss the reminders for all listed incidences"));
177 mUser1Button
->setText(i18nc("@action:button", "Edit..."));
178 mUser1Button
->setToolTip(i18nc("@info:tooltip",
179 "Edit the selected incidence"));
180 mOkButton
->setText(i18nc("@action:button", "Suspend"));
181 mOkButton
->setToolTip(i18nc("@info:tooltip",
182 "Suspend the reminders for the selected incidences "
183 "by the specified interval"));
185 // Try to keep the dialog small and non-obtrusive.
186 setMinimumWidth(575);
187 setMinimumHeight(300);
189 QVBoxLayout
*mTopLayout
= new QVBoxLayout(topBox
);
191 QLabel
*label
= new QLabel(
194 "Click on a title to toggle the details viewer for that item"),
196 mTopLayout
->addWidget(label
);
198 mIncidenceTree
= new ReminderTree(topBox
);
199 mIncidenceTree
->setColumnCount(3);
200 mIncidenceTree
->setSortingEnabled(true);
201 const QStringList headerLabels
=
202 (QStringList(i18nc("@title:column reminder title", "Title"))
203 << i18nc("@title:column happens at date/time", "Date Time")
204 << i18nc("@title:column trigger date/time", "Trigger Time"));
205 mIncidenceTree
->setHeaderLabels(headerLabels
);
206 mIncidenceTree
->headerItem()->setToolTip(
208 i18nc("@info:tooltip", "The event or to-do title"));
209 mIncidenceTree
->headerItem()->setToolTip(
211 i18nc("@info:tooltip", "The reminder is set for this date/time"));
212 mIncidenceTree
->headerItem()->setToolTip(
214 i18nc("@info:tooltip", "The date/time the reminder was triggered"));
216 mIncidenceTree
->setWordWrap(true);
217 mIncidenceTree
->setAllColumnsShowFocus(true);
218 mIncidenceTree
->setSelectionMode(QAbstractItemView::ExtendedSelection
);
219 mIncidenceTree
->setRootIsDecorated(false);
221 mTopLayout
->addWidget(mIncidenceTree
);
223 connect(mIncidenceTree
, &QTreeWidget::itemClicked
, this, &AlarmDialog::update
);
224 connect(mIncidenceTree
, &QTreeWidget::itemDoubleClicked
, this, &AlarmDialog::edit
);
225 connect(mIncidenceTree
, &ReminderTree::itemSelectionChanged
, this, &AlarmDialog::updateButtons
);
227 mDetailView
= new CalendarSupport::IncidenceViewer(mCalendar
.data(), topBox
);
229 s
= xi18nc("@info default incidence details string",
230 "<emphasis>Select an event or to-do from the list above "
231 "to view its details here.</emphasis>");
232 mDetailView
->setDefaultMessage(s
);
233 mTopLayout
->addWidget(mDetailView
);
237 QWidget
*suspendBox
= new QWidget(topBox
);
238 QHBoxLayout
*suspendBoxHBoxLayout
= new QHBoxLayout(suspendBox
);
239 suspendBoxHBoxLayout
->setMargin(0);
240 mTopLayout
->addWidget(suspendBox
);
242 QLabel
*l
= new QLabel(i18nc("@label:spinbox", "Suspend &duration:"), suspendBox
);
243 suspendBoxHBoxLayout
->addWidget(l
);
245 mSuspendSpin
= new QSpinBox(suspendBox
);
246 suspendBoxHBoxLayout
->addWidget(mSuspendSpin
);
247 mSuspendSpin
->setRange(1, 9999);
248 mSuspendSpin
->setValue(defSuspendVal
); // default suspend duration
249 mSuspendSpin
->setToolTip(
250 i18nc("@info:tooltip",
251 "Suspend the reminders by this amount of time"));
252 mSuspendSpin
->setWhatsThis(
253 i18nc("@info:whatsthis",
254 "Each reminder for the selected incidences will be suspended "
255 "by this number of time units. You can choose the time units "
256 "(typically minutes) in the adjacent selector."));
258 l
->setBuddy(mSuspendSpin
);
260 mSuspendUnit
= new KComboBox(suspendBox
);
261 suspendBoxHBoxLayout
->addWidget(mSuspendUnit
);
262 mSuspendUnit
->addItem(i18nc("@item:inlistbox suspend in terms of minutes", "minute(s)"));
263 mSuspendUnit
->addItem(i18nc("@item:inlistbox suspend in terms of hours", "hour(s)"));
264 mSuspendUnit
->addItem(i18nc("@item:inlistbox suspend in terms of days", "day(s)"));
265 mSuspendUnit
->addItem(i18nc("@item:inlistbox suspend in terms of weeks", "week(s)"));
266 mSuspendUnit
->setToolTip(
267 i18nc("@info:tooltip",
268 "Suspend the reminders using this time unit"));
269 mSuspendUnit
->setWhatsThis(
270 i18nc("@info:whatsthis",
271 "Each reminder for the selected incidences will be suspended "
272 "using this time unit. You can set the number of time units "
273 "in the adjacent number entry input."));
275 mSuspendUnit
->setCurrentIndex(defSuspendUnit
);
277 connect(&mSuspendTimer
, &QTimer::timeout
, this, &AlarmDialog::wakeUp
);
279 connect(mOkButton
, &QPushButton::clicked
, this, &AlarmDialog::slotOk
);
280 connect(mUser1Button
, &QPushButton::clicked
, this, &AlarmDialog::slotUser1
);
281 connect(mUser2Button
, &QPushButton::clicked
, this, &AlarmDialog::slotUser2
);
282 connect(mUser3Button
, &QPushButton::clicked
, this, &AlarmDialog::slotUser3
);
284 mIdentityManager
= new CalendarSupport::IdentityManager
;
287 AlarmDialog::~AlarmDialog()
289 mIncidenceTree
->clear();
290 delete mIdentityManager
;
293 ReminderTreeItem
*AlarmDialog::searchByItem(const Akonadi::Item
&incidence
)
295 ReminderTreeItem
*found
= 0;
296 QTreeWidgetItemIterator
it(mIncidenceTree
);
298 ReminderTreeItem
*item
= static_cast<ReminderTreeItem
*>(*it
);
299 if (item
->mIncidence
== incidence
) {
308 static QString
cleanSummary(const QString
&summary
)
310 static QString etc
= i18nc("@label an elipsis", "...");
312 QString retStr
= summary
;
313 retStr
.replace(QLatin1Char('\n'), QLatin1Char(' '));
314 if (retStr
.length() > maxLen
) {
315 maxLen
-= etc
.length();
316 retStr
= retStr
.left(maxLen
);
322 void AlarmDialog::addIncidence(const Akonadi::Item
&incidenceitem
,
323 const QDateTime
&reminderAt
,
324 const QString
&displayText
)
326 Incidence::Ptr incidence
= CalendarSupport::incidence(incidenceitem
);
327 ReminderTreeItem
*item
= searchByItem(incidenceitem
);
329 item
= new ReminderTreeItem(incidenceitem
, mIncidenceTree
);
331 item
->mNotified
= false;
332 item
->mHappening
= KDateTime();
333 item
->mRemindAt
= reminderAt
;
334 item
->mTrigger
= KDateTime::currentLocalDateTime();
335 item
->mDisplayText
= displayText
;
336 item
->setText(0, cleanSummary(incidence
->summary()));
339 const KDateTime dateTime
= triggerDateForIncidence(incidence
, reminderAt
,
342 if (incidence
->type() == Incidence::TypeEvent
) {
343 item
->setIcon(0, QIcon::fromTheme(QStringLiteral("view-calendar-day")));
344 } else if (incidence
->type() == Incidence::TypeTodo
) {
345 item
->setIcon(0, QIcon::fromTheme(QStringLiteral("view-calendar-tasks")));
348 item
->mHappening
= dateTime
;
349 item
->setText(1, displayStr
);
351 item
->setText(2, IncidenceFormatter::dateTimeToString(
352 item
->mTrigger
, false, true, KDateTime::Spec::LocalZone()));
354 IncidenceFormatter::toolTipStr(
355 CalendarSupport::displayName(mCalendar
.data(), incidenceitem
.parentCollection()),
357 item
->mRemindAt
.date(), true,
358 KDateTime::Spec::LocalZone());
359 if (!item
->mDisplayText
.isEmpty()) {
360 tip
+= QLatin1String("<br>") + item
->mDisplayText
;
362 item
->setToolTip(0, tip
);
363 item
->setToolTip(1, tip
);
364 item
->setToolTip(2, tip
);
365 item
->setData(0, QTreeWidgetItem::UserType
, false);
367 mIncidenceTree
->setCurrentItem(item
);
372 void AlarmDialog::slotOk()
377 void AlarmDialog::slotUser1()
379 const ReminderList selection
= selectedItems();
380 if (!selection
.isEmpty()) {
381 ReminderTreeItem
*item
= selection
.first();
382 if (mCalendar
->hasRight(item
->mIncidence
, Akonadi::Collection::CanChangeItem
)) {
388 void AlarmDialog::slotUser2()
393 void AlarmDialog::slotUser3()
398 void AlarmDialog::dismissCurrent()
400 dismiss(selectedItems());
402 if (activeCount() == 0) {
407 Q_EMIT
reminderCount(activeCount());
410 void AlarmDialog::dismissAll()
412 ReminderList selections
;
414 QTreeWidgetItemIterator
it(mIncidenceTree
);
416 if (!(*it
)->isDisabled()) { //do not disable suspended reminders
417 selections
.append(static_cast<ReminderTreeItem
*>(*it
));
425 Q_EMIT
reminderCount(activeCount());
428 void AlarmDialog::dismiss(ReminderList selections
)
430 QList
<Akonadi::Item::Id
> ids
;
431 ids
.reserve(selections
.count());
432 for (ReminderList::Iterator it
= selections
.begin(); it
!= selections
.end(); ++it
) {
433 qCDebug(KOALARMCLIENT_LOG
) << "removing " << CalendarSupport::incidence((*it
)->mIncidence
)->summary();
434 if (mIncidenceTree
->itemBelow(*it
)) {
435 mIncidenceTree
->setCurrentItem(mIncidenceTree
->itemBelow(*it
));
436 } else if (mIncidenceTree
->itemAbove(*it
)) {
437 mIncidenceTree
->setCurrentItem(mIncidenceTree
->itemAbove(*it
));
439 mIncidenceTree
->removeItemWidget(*it
, 0);
440 ids
.append((*it
)->mIncidence
.id());
444 removeFromConfig(ids
);
447 void AlarmDialog::edit()
449 ReminderList selection
= selectedItems();
450 if (selection
.count() != 1) {
453 Incidence::Ptr incidence
= CalendarSupport::incidence(selection
.first()->mIncidence
);
454 if (!mCalendar
->hasRight(selection
.first()->mIncidence
, Akonadi::Collection::CanChangeItem
)) {
458 "\"%1\" is a read-only item so modifications are not possible.",
459 cleanSummary(incidence
->summary())));
463 openIncidenceEditorNG(selection
.first()->mIncidence
);
466 void AlarmDialog::suspend()
468 if (!isVisible()) { //do nothing if the dialog is hidden
473 switch (mSuspendUnit
->currentIndex()) {
486 ReminderTreeItem
*selitem
= 0;
487 QTreeWidgetItemIterator
it(mIncidenceTree
);
489 if ((*it
)->isSelected() && !(*it
)->isDisabled()) { //suspend selected, non-suspended reminders
490 (*it
)->setSelected(false);
491 (*it
)->setDisabled(true);
492 ReminderTreeItem
*item
= static_cast<ReminderTreeItem
*>(*it
);
493 item
->mRemindAt
= QDateTime::currentDateTime().addSecs(unit
* mSuspendSpin
->value());
494 item
->mHappening
= KDateTime(item
->mRemindAt
, KDateTime::Spec::LocalZone());
495 item
->mNotified
= false;
496 (*it
)->setText(1, KLocale::global()->formatDateTime(item
->mHappening
));
503 if (mIncidenceTree
->itemBelow(selitem
)) {
504 mIncidenceTree
->setCurrentItem(mIncidenceTree
->itemBelow(selitem
));
505 } else if (mIncidenceTree
->itemAbove(selitem
)) {
506 mIncidenceTree
->setCurrentItem(mIncidenceTree
->itemAbove(selitem
));
510 // save suspended alarms too so they can be restored on restart
515 if (activeCount() == 0) {
520 Q_EMIT
reminderCount(activeCount());
523 void AlarmDialog::setTimer()
525 int nextReminderAt
= -1;
527 QTreeWidgetItemIterator
it(mIncidenceTree
);
529 ReminderTreeItem
*item
= static_cast<ReminderTreeItem
*>(*it
);
530 if (item
->mRemindAt
> QDateTime::currentDateTime()) {
531 const int secs
= QDateTime::currentDateTime().secsTo(item
->mRemindAt
);
532 nextReminderAt
= nextReminderAt
<= 0 ? secs
: qMin(nextReminderAt
, secs
);
537 if (nextReminderAt
>= 0) {
538 mSuspendTimer
.stop();
539 mSuspendTimer
.start(1000 * (nextReminderAt
+ 1));
540 mSuspendTimer
.setSingleShot(true);
544 void AlarmDialog::show()
546 mIncidenceTree
->resizeColumnToContents(0);
547 mIncidenceTree
->resizeColumnToContents(1);
548 mIncidenceTree
->resizeColumnToContents(2);
549 mIncidenceTree
->sortItems(1, Qt::AscendingOrder
);
551 // select the first item that hasn't already been notified
552 QTreeWidgetItemIterator
it(mIncidenceTree
);
554 ReminderTreeItem
*item
= static_cast<ReminderTreeItem
*>(*it
);
555 if (!item
->mNotified
) {
556 (*it
)->setSelected(true);
562 // reset the default suspend time
563 // Allen: commented-out the following lines on 17 Sept 2013
564 // mSuspendSpin->setValue( defSuspendVal );
565 // mSuspendUnit->setCurrentIndex( defSuspendUnit );
568 if (!mPos
.isNull()) {
571 KWindowSystem::unminimizeWindow(winId(), false);
572 KWindowSystem::setState(winId(), NET::KeepAbove
| NET::DemandsAttention
);
573 KWindowSystem::setOnAllDesktops(winId(), true);
574 KWindowSystem::activateWindow(winId());
576 // Audio, Procedure, and EMail alarms
580 void AlarmDialog::suspendAll()
582 mIncidenceTree
->clearSelection();
583 QTreeWidgetItemIterator
it(mIncidenceTree
);
585 // first, select all non-suspended reminders
587 if (!(*it
)->isDisabled()) { //do not suspend suspended reminders
588 (*it
)->setSelected(true);
593 //suspend all selected reminders
597 void AlarmDialog::eventNotification()
604 QTreeWidgetItemIterator
it(mIncidenceTree
);
606 ReminderTreeItem
*item
= static_cast<ReminderTreeItem
*>(*it
);
608 if (item
->isDisabled() || item
->mNotified
) {
609 //skip suspended reminders or reminders that have been notified
613 item
->mNotified
= true;
614 Incidence::Ptr incidence
= CalendarSupport::incidence(item
->mIncidence
);
615 Alarm::List alarms
= incidence
->alarms();
616 Alarm::List::ConstIterator ait
;
617 for (ait
= alarms
.constBegin(); ait
!= alarms
.constEnd(); ++ait
) {
618 Alarm::Ptr alarm
= *ait
;
619 // FIXME: Check whether this should be done for all multiple alarms
620 if (alarm
->type() == Alarm::Procedure
) {
621 // FIXME: Add a message box asking whether the procedure should really be executed
622 qCDebug(KOALARMCLIENT_LOG
) << "Starting program: '" << alarm
->programFile() << "'";
624 QString program
= alarm
->programFile();
626 // if the program name contains spaces escape it
627 if (program
.contains(QLatin1Char(' ')) &&
628 !(program
.startsWith(QLatin1Char('\"')) &&
629 program
.endsWith(QLatin1Char('\"')))) {
630 program
= QLatin1Char('\"') + program
+ QLatin1Char('\"');
633 QProcess::startDetached(program
+ QLatin1Char(' ') + alarm
->programArguments());
634 } else if (alarm
->type() == Alarm::Audio
) {
636 Phonon::MediaObject
*player
=
637 Phonon::createPlayer(Phonon::NotificationCategory
,
638 QUrl::fromLocalFile(alarm
->audioFile()));
639 player
->setParent(this);
640 connect(player
, &Phonon::MediaObject::finished
, player
, &Phonon::MediaObject::deleteLater
);
642 } else if (alarm
->type() == Alarm::Email
) {
643 QString from
= CalendarSupport::KCalPrefs::instance()->email();
644 Identity id
= mIdentityManager
->identityForAddress(from
);
646 if (alarm
->mailAddresses().isEmpty()) {
649 const Person::List addresses
= alarm
->mailAddresses();
651 add
.reserve(addresses
.count());
652 for (Person::List::ConstIterator it
= addresses
.constBegin();
653 it
!= addresses
.constEnd(); ++it
) {
654 add
<< (*it
)->fullName();
656 to
= add
.join(QStringLiteral(", "));
661 Akonadi::Item parentItem
= mCalendar
->item(alarm
->parentUid());
662 Incidence::Ptr parent
= CalendarSupport::incidence(parentItem
);
664 if (alarm
->mailSubject().isEmpty()) {
665 if (parent
->summary().isEmpty()) {
666 subject
= i18nc("@title", "Reminder");
668 subject
= i18nc("@title", "Reminder: %1", cleanSummary(parent
->summary()));
671 subject
= i18nc("@title", "Reminder: %1", alarm
->mailSubject());
675 IncidenceFormatter::mailBodyStr(
676 parent
.staticCast
<IncidenceBase
>(), KSystemTimeZones::local());
677 if (!alarm
->mailText().isEmpty()) {
678 body
+= QLatin1Char('\n') + alarm
->mailText();
680 //TODO: support attachments
681 KOrg::MailClient mailer
;
682 mailer
.send(id
, from
, to
, QString(), subject
, body
, true, false, QString(),
683 MailTransport::TransportManager::self()->defaultTransportName());
688 if (!beeped
&& found
) {
689 KNotification::beep();
693 void AlarmDialog::wakeUp()
695 bool activeReminders
= false;
696 QTreeWidgetItemIterator
it(mIncidenceTree
);
697 QTreeWidgetItem
*firstItem
= 0;
702 ReminderTreeItem
*item
= static_cast<ReminderTreeItem
*>(*it
);
703 Incidence::Ptr incidence
= CalendarSupport::incidence(item
->mIncidence
);
705 if (item
->mRemindAt
<= QDateTime::currentDateTime()) {
706 if (item
->isDisabled()) { //do not wakeup non-suspended reminders
707 item
->setDisabled(false);
708 item
->setSelected(false);
710 activeReminders
= true;
712 item
->setDisabled(true);
718 if (activeReminders
) {
722 showDetails(firstItem
);
723 Q_EMIT
reminderCount(activeCount());
726 void AlarmDialog::slotSave()
728 KSharedConfig::Ptr config
= KSharedConfig::openConfig();
729 KConfigGroup
generalConfig(config
, "General");
730 int numReminders
= 0;
732 QTreeWidgetItemIterator
it(mIncidenceTree
);
734 ReminderTreeItem
*item
= static_cast<ReminderTreeItem
*>(*it
);
735 KConfigGroup
incidenceConfig(config
,
736 QStringLiteral("Incidence-%1").arg(numReminders
+ 1));
738 Incidence::Ptr incidence
= CalendarSupport::incidence(item
->mIncidence
);
739 incidenceConfig
.writeEntry("AkonadiUrl", item
->mIncidence
.url());
740 incidenceConfig
.writeEntry("RemindAt", item
->mRemindAt
);
745 generalConfig
.writeEntry("Reminders", numReminders
);
746 generalConfig
.writeEntry("Position", pos());
750 AlarmDialog::ReminderList
AlarmDialog::selectedItems() const
754 QTreeWidgetItemIterator
it(mIncidenceTree
);
756 if ((*it
)->isSelected()) {
757 list
.append(static_cast<ReminderTreeItem
*>(*it
));
764 int AlarmDialog::activeCount()
767 QTreeWidgetItemIterator
it(mIncidenceTree
);
769 if (!(*it
)->isDisabled()) { //suspended reminders are non-active
774 qCDebug(KOALARMCLIENT_LOG
) << "computed " << count
<< " active reminders";
778 void AlarmDialog::closeEvent(QCloseEvent
*)
784 void AlarmDialog::updateButtons()
786 const ReminderList selection
= selectedItems();
787 const int count
= selection
.count();
788 const bool enabled
= (count
> 0);
789 qCDebug(KOALARMCLIENT_LOG
) << "selected items=" << count
;
790 mUser3Button
->setEnabled(enabled
);
791 mOkButton
->setEnabled(enabled
);
793 ReminderTreeItem
*item
= selection
.first();
794 mUser1Button
->setEnabled(mCalendar
->hasRight(item
->mIncidence
, Akonadi::Collection::CanChangeItem
));
796 mUser1Button
->setEnabled(false);
800 void AlarmDialog::toggleDetails(QTreeWidgetItem
*item
)
806 if (!mDetailView
->isHidden()) {
807 if (mLastItem
== item
) {
808 resize(size().width(), size().height() - mDetailView
->height() - 50);
814 resize(size().width(), size().height() + mDetailView
->height() + 50);
821 void AlarmDialog::showDetails(QTreeWidgetItem
*item
)
827 ReminderTreeItem
*reminderItem
= dynamic_cast<ReminderTreeItem
*>(item
);
830 mDetailView
->setIncidence(Akonadi::Item());
832 if (!reminderItem
->mDisplayText
.isEmpty()) {
833 QString txt
= QLatin1String("<qt><p><b>") + reminderItem
->mDisplayText
+ QLatin1String("</b></p></qt>");
834 mDetailView
->setHeaderText(txt
);
836 mDetailView
->setHeaderText(QString());
838 Incidence::Ptr incidence
= CalendarSupport::incidence(reminderItem
->mIncidence
);
839 mDetailView
->setIncidence(reminderItem
->mIncidence
, reminderItem
->mRemindAt
.date());
843 void AlarmDialog::update()
847 const ReminderList selection
= selectedItems();
848 if (!selection
.isEmpty()) {
849 ReminderTreeItem
*item
= selection
.first();
850 mUser1Button
->setEnabled((mCalendar
->hasRight(item
->mIncidence
, Akonadi::Collection::CanChangeItem
)) && (selection
.count() == 1));
855 void AlarmDialog::accept()
857 if (activeCount() == 0) {
864 KDateTime
AlarmDialog::triggerDateForIncidence(const Incidence::Ptr
&incidence
,
865 const QDateTime
&reminderAt
,
870 if (incidence
->alarms().isEmpty()) {
874 Alarm::Ptr alarm
= incidence
->alarms().at(0);
876 if (incidence
->recurs()) {
877 result
= incidence
->recurrence()->getNextDateTime(
878 KDateTime(reminderAt
, KDateTime::Spec::LocalZone()));
880 displayStr
= KLocale::global()->formatDateTime(result
.toLocalZone());
883 if (!result
.isValid()) {
884 result
= incidence
->dateTime(Incidence::RoleAlarm
);
885 displayStr
= IncidenceFormatter::dateTimeToString(result
, false,
887 KDateTime::Spec::LocalZone());
893 void AlarmDialog::slotCalendarChanged()
895 KCalCore::Incidence::List incidences
= mCalendar
->incidences();
896 Akonadi::Item::List items
= mCalendar
->itemList(incidences
);
897 for (Akonadi::Item::List::ConstIterator it
= items
.constBegin();
898 it
!= items
.constEnd(); ++it
) {
899 ReminderTreeItem
*item
= searchByItem(*it
);
902 Incidence::Ptr incidence
= CalendarSupport::incidence(*it
);
905 // Yes, alarms can be empty, if someone edited the incidence and removed all alarms
906 if (!incidence
->alarms().isEmpty()) {
907 const KDateTime dateTime
= triggerDateForIncidence(incidence
,
911 const QString summary
= cleanSummary(incidence
->summary());
913 if (displayStr
!= item
->text(1) || summary
!= item
->text(0)) {
914 item
->setText(1, displayStr
);
915 item
->setText(0, summary
);
922 void AlarmDialog::keyPressEvent(QKeyEvent
*e
)
924 const int key
= e
->key() | e
->modifiers();
926 if (key
== Qt::Key_Enter
|| key
== Qt::Key_Return
) {
931 QDialog::keyPressEvent(e
);
934 bool AlarmDialog::openIncidenceEditorThroughKOrganizer(const Incidence::Ptr
&incidence
)
936 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.korganizer"))) {
937 if (KToolInvocation::startServiceByDesktopName(QStringLiteral("korganizer"), QString())) {
941 "Could not start KOrganizer so editing is not possible."));
945 org::kde::korganizer::Korganizer
korganizer(
946 QStringLiteral("org.kde.korganizer"), QStringLiteral("/Korganizer"), QDBusConnection::sessionBus());
948 qCDebug(KOALARMCLIENT_LOG
) << "editing incidence " << incidence
->summary();
949 if (!korganizer
.editIncidence(incidence
->uid())) {
953 "An internal KOrganizer error occurred attempting to modify \"%1\"",
954 cleanSummary(incidence
->summary())));
957 // get desktop # where korganizer (or kontact) runs
959 QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.kontact")) ?
960 QStringLiteral("kontact/MainWindow_1") : QStringLiteral("korganizer/MainWindow_1");
961 QDBusInterface
korganizerObj(QStringLiteral("org.kde.korganizer"), QLatin1Char('/') + object
);
963 QDBusReply
<int> reply
= korganizerObj
.call(QStringLiteral("winId"));
964 if (reply
.isValid()) {
966 auto winInfo
= KWindowInfo(window
, NET::WMDesktop
);
967 int desktop
= winInfo
.desktop();
968 if (KWindowSystem::currentDesktop() == desktop
) {
969 KWindowSystem::minimizeWindow(winId(), false);
971 KWindowSystem::setCurrentDesktop(desktop
);
973 KWindowSystem::activateWindow(winInfo
.transientFor());
975 #elif defined(Q_OS_WIN)
976 // WId is a typedef to a void* on windows
977 QDBusReply
<qlonglong
> reply
= korganizerObj
.call(QStringLiteral("winId"));
978 if (reply
.isValid()) {
979 qlonglong window
= reply
;
980 KWindowSystem::minimizeWindow(winId(), false);
981 KWindowSystem::allowExternalProcessWindowActivation();
982 KWindowSystem::activateWindow(reinterpret_cast<WId
>(window
));
990 bool AlarmDialog::openIncidenceEditorNG(const Akonadi::Item
&item
)
992 Incidence::Ptr incidence
= CalendarSupport::incidence(item
);
993 IncidenceEditorNG::IncidenceDialog
*dialog
=
994 IncidenceEditorNG::IncidenceDialogFactory::create(
995 false, /*doesn't need initial saving*/
996 incidence
->type(), 0, this);
1001 void AlarmDialog::removeFromConfig(const QList
<Akonadi::Item::Id
> &ids
)
1003 KSharedConfig::Ptr config
= KSharedConfig::openConfig();
1004 KConfigGroup
genGroup(config
, "General");
1006 const int oldNumReminders
= genGroup
.readEntry("Reminders", 0);
1008 QVector
<ConfItem
> newReminders
;
1009 // Delete everything
1010 for (int i
= 1; i
<= oldNumReminders
; ++i
) {
1011 const QString
group(QStringLiteral("Incidence-%1").arg(i
));
1012 KConfigGroup
incGroup(config
, group
);
1013 const QString uid
= incGroup
.readEntry("UID");
1014 const QDateTime remindAtDate
= incGroup
.readEntry("RemindAt", QDateTime());
1015 const QUrl akonadiUrl
= incGroup
.readEntry("AkonadiUrl");
1016 const Akonadi::Item::Id id
= Akonadi::Item::fromUrl(akonadiUrl
).id();
1017 if (!ids
.contains(id
)) {
1019 ci
.akonadiUrl
= akonadiUrl
;
1020 ci
.remindAt
= remindAtDate
;
1022 newReminders
.append(ci
);
1024 config
->deleteGroup(group
);
1027 const int newRemindersCount(newReminders
.count());
1028 genGroup
.writeEntry("Reminders", newRemindersCount
);
1030 //Write everything except those which have an uid we don't want
1031 for (int i
= 0; i
< newRemindersCount
; ++i
) {
1032 const QString
group(QStringLiteral("Incidence-%1").arg(i
+ 1));
1033 KConfigGroup
incGroup(config
, group
);
1034 const ConfItem conf
= newReminders
.at(i
);
1035 incGroup
.writeEntry("UID", conf
.uid
);
1036 incGroup
.writeEntry("RemindAt", conf
.remindAt
);
1037 incGroup
.writeEntry("AkonadiUrl", conf
.akonadiUrl
);