Commit 297bb13a authored by Claudio Cambra's avatar Claudio Cambra
Browse files

Add an alarm client to Kalendar

parent e28ae8de
Pipeline #101426 passed with stage
in 4 minutes and 58 seconds
......@@ -8,9 +8,9 @@ project(kalendar VERSION 0.2.1)
set(QT_MIN_VERSION "5.15.2")
set(KF5_MIN_VERSION "5.86.0")
set(AKONADI_VERSION "5.15.0")
set(CALENDARSUPPORT_LIB_VERSION "5.15")
set(EVENTVIEW_LIB_VERSION "5.15.0")
set(AKONADI_VERSION "5.18.0")
set(CALENDARSUPPORT_LIB_VERSION "5.18")
set(EVENTVIEW_LIB_VERSION "5.18.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.84")
set(CMAKE_CXX_STANDARD 17)
......@@ -43,8 +43,8 @@ ecm_setup_version(${PROJECT_VERSION}
)
################# Find dependencies #################
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg Location)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 DBusAddons I18n CalendarCore ConfigWidgets WindowSystem CoreAddons Contacts ItemModels XmlGui)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg Location DBus)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 DBusAddons I18n CalendarCore ConfigWidgets WindowSystem CoreAddons Contacts ItemModels XmlGui Notifications IconThemes)
find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED)
find_package(KF5AkonadiContact ${AKONADI_CONTACT_VERSION} CONFIG REQUIRED)
find_package(KF5CalendarSupport ${CALENDARSUPPORT_LIB_VERSION} CONFIG REQUIRED)
......@@ -59,6 +59,7 @@ add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050f02)
add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x055800)
add_subdirectory(src)
add_subdirectory(kalendarac)
install(PROGRAMS org.kde.kalendar.desktop DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES org.kde.kalendar.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
......
# SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
#
# SPDX-License-Identifier: BSD-2-Clause
set(kalendarac_SRCS
kalendaralarmclient.cpp
alarmdockwindow.cpp
alarmnotification.cpp
notificationhandler.cpp
kalendaracmain.cpp
)
qt5_add_dbus_adaptor(kalendarac_SRCS org.kde.kalendarac.xml kalendaralarmclient.h KalendarAlarmClient)
add_executable(kalendarac ${kalendarac_SRCS} ${RESOURCES})
target_link_libraries(kalendarac
KF5::CoreAddons
KF5::ConfigCore
KF5::CalendarCore
KF5::DBusAddons
KF5::AkonadiCore
KF5::CalendarSupport
KF5::I18n
KF5::Notifications
KF5::IconThemes
KF5::Service
KF5::WindowSystem
Qt5::Core
Qt5::DBus
)
install(TARGETS
kalendarac ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}
)
install(FILES
org.kde.kalendarac.desktop
DESTINATION ${KDE_INSTALL_AUTOSTARTDIR}
)
install(FILES kalendarac.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR})
set(SERV_EXEC ${KDE_INSTALL_FULL_BINDIR}/kalendarac)
configure_file(org.kde.kalendarac.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kalendarac.service)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kalendarac.service DESTINATION ${KDE_INSTALL_FULL_DBUSSERVICEDIR})
// SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
// SPDX-FileCopyrightText: 2008-2009 Allen Winter <winter@kde.org>
// SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Qt-Commercial-exception-1.0
#include "alarmdockwindow.h"
#include <KConfigGroup>
#include <KIconEffect>
#include <KIconLoader>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDebug>
#include <QMenu>
AlarmDockWindow::AlarmDockWindow()
: KStatusNotifierItem(nullptr)
{
// Read the autostart status from the config file
KConfigGroup config(KSharedConfig::openConfig(), "General");
const bool autostartSet = config.hasKey("Autostart");
const bool autostart = config.readEntry("Autostart", true);
// const bool grabFocus = config.readEntry("GrabFocus", false);
const bool alarmsEnabled = config.readEntry("Enabled", true);
// Don't mention Daemon here since it's a technical
// term the user doesn't care about
mName = i18nc("@title:window", "Kalendar Reminders");
setToolTipTitle(mName);
setToolTipIconByName(QStringLiteral("kalendarac"));
setTitle(mName);
// Set up icons
const QIcon iconEnabled = QIcon::fromTheme(QStringLiteral("org.kde.kalendar"));
KIconLoader loader;
QImage iconDisabled = iconEnabled.pixmap(loader.currentSize(KIconLoader::Panel)).toImage();
KIconEffect::toGray(iconDisabled, 1.0);
mIconDisabled = QIcon(QPixmap::fromImage(iconDisabled));
changeSystrayIcon(alarmsEnabled);
// Set up the context menu
mAlarmsEnabled = contextMenu()->addAction(i18nc("@action:inmenu", "Enable Reminders"));
connect(mAlarmsEnabled, &QAction::toggled, this, &AlarmDockWindow::toggleAlarmsEnabled);
mAlarmsEnabled->setCheckable(true);
mAutostart = contextMenu()->addAction(i18nc("@action:inmenu", "Start Reminder Daemon at Login"));
connect(mAutostart, &QAction::toggled, this, &AlarmDockWindow::toggleAutostart);
mAutostart->setCheckable(true);
mAlarmsEnabled->setChecked(alarmsEnabled);
mAutostart->setChecked(autostart);
// Disable standard quit behaviour. We have to intercept the quit even,
// if the main window is hidden.
QAction *act = action(QStringLiteral("quit"));
if (act) {
disconnect(act, SIGNAL(triggered(bool)), this, SLOT(maybeQuit()));
connect(act, &QAction::triggered, this, &AlarmDockWindow::slotQuit);
} else {
qDebug() << "No Quit standard action.";
}
mAutostartSet = autostartSet;
}
AlarmDockWindow::~AlarmDockWindow()
{
}
void AlarmDockWindow::slotUpdate(int reminders)
{
const bool actif = (reminders > 0);
if (actif) {
setToolTip(QStringLiteral("kalendarac"), mName, i18ncp("@info:status", "There is 1 active reminder.", "There are %1 active reminders.", reminders));
} else {
setToolTip(QStringLiteral("kalendarac"), mName, i18nc("@info:status", "No active reminders."));
}
}
void AlarmDockWindow::toggleAlarmsEnabled(bool checked)
{
changeSystrayIcon(checked);
KConfigGroup config(KSharedConfig::openConfig(), "General");
config.writeEntry("Enabled", checked);
config.sync();
}
void AlarmDockWindow::toggleAutostart(bool checked)
{
// qCDebug(KOALARMCLIENT_LOG);
mAutostartSet = true;
enableAutostart(checked);
}
void AlarmDockWindow::toggleGrabFocus(bool checked)
{
KConfigGroup config(KSharedConfig::openConfig(), "General");
config.writeEntry("GrabFocus", checked);
}
void AlarmDockWindow::slotSuspendAll()
{
Q_EMIT suspendAllSignal();
}
void AlarmDockWindow::slotDismissAll()
{
Q_EMIT dismissAllSignal();
}
void AlarmDockWindow::enableAutostart(bool enable)
{
KConfigGroup config(KSharedConfig::openConfig(), "General");
config.writeEntry("Autostart", enable);
config.sync();
}
void AlarmDockWindow::activate(const QPoint &pos)
{
Q_UNUSED(pos)
QDBusConnection::sessionBus().interface()->startService(QStringLiteral("org.kde.kalendar"));
}
void AlarmDockWindow::slotQuit()
{
if (mAutostartSet == true) {
const int result = KMessageBox::warningContinueCancel(associatedWidget(),
xi18nc("@info",
"Do you want to quit the Kalendar reminder daemon?<nl/>"
"<note> you will not get calendar reminders unless the daemon is running.</note>"),
i18nc("@title:window", "Close Kalendar Reminder Daemon"),
KStandardGuiItem::quit());
if (result == KMessageBox::Continue) {
Q_EMIT quitSignal();
}
} else {
const int result = KMessageBox::questionYesNoCancel(associatedWidget(),
xi18nc("@info",
"Do you want to start the Kalendar reminder daemon at login?<nl/>"
"<note> you will not get calendar reminders unless the daemon is running.</note>"),
i18nc("@title:window", "Close Kalendar Reminder Daemon"),
KGuiItem(i18nc("@action:button start the reminder daemon", "Start")),
KGuiItem(i18nc("@action:button do not start the reminder daemon", "Do Not Start")),
KStandardGuiItem::cancel(),
QStringLiteral("AskForStartAtLogin"));
bool autostart = true;
if (result == KMessageBox::No) {
autostart = false;
}
enableAutostart(autostart);
if (result != KMessageBox::Cancel) {
Q_EMIT quitSignal();
}
}
}
void AlarmDockWindow::changeSystrayIcon(bool alarmsEnabled)
{
if (alarmsEnabled) {
setIconByName(QStringLiteral("org.kde.kalendar"));
} else {
setIconByPixmap(mIconDisabled.pixmap(22, 22));
}
}
// SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
// SPDX-FileCopyrightText: 2008-2009 Allen Winter <winter@kde.org>
// SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Qt-Commercial-exception-1.0
#pragma once
#include <KStatusNotifierItem>
#include <QAction>
#include <QIcon>
class AlarmDockWindow : public KStatusNotifierItem
{
Q_OBJECT
public:
AlarmDockWindow();
~AlarmDockWindow() override;
void enableAutostart(bool enabled);
public Q_SLOTS:
void toggleAlarmsEnabled(bool checked);
void toggleAutostart(bool checked);
void toggleGrabFocus(bool checked);
void slotUpdate(int reminders);
Q_SIGNALS:
void quitSignal();
void suspendAllSignal();
void dismissAllSignal();
void showReminderSignal();
protected Q_SLOTS:
void activate(const QPoint &pos) override;
void slotQuit();
void slotSuspendAll();
void slotDismissAll();
private:
void changeSystrayIcon(bool alarmsEnabled);
QIcon mIconDisabled;
QString mName;
QAction *mAlarmsEnabled = nullptr;
QAction *mAutostart = nullptr;
QAction *mShow = nullptr;
bool mAutostartSet = false;
};
/*
* SPDX-FileCopyrightText: 2019 Dimitris Kardarakos <dimkard@posteo.net>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "alarmnotification.h"
#include "notificationhandler.h"
#include <KLocalizedString>
#include <QDebug>
AlarmNotification::AlarmNotification(NotificationHandler *handler, const QString &uid)
: m_uid{uid}
, m_remind_at{QDateTime()}
, m_notification_handler{handler}
{
m_notification = new KNotification(QStringLiteral("alarm"));
m_notification->setActions({i18n("Remind in 5 mins"), i18n("Dismiss")});
connect(m_notification, &KNotification::action1Activated, this, &AlarmNotification::suspend);
connect(m_notification, &KNotification::action2Activated, this, &AlarmNotification::dismiss);
connect(this, &AlarmNotification::suspend, m_notification_handler, [this]() {
m_notification_handler->suspend(this);
});
connect(this, &AlarmNotification::dismiss, m_notification_handler, [this]() {
m_notification_handler->dismiss(this);
});
}
AlarmNotification::~AlarmNotification()
{
delete m_notification;
}
void AlarmNotification::send() const
{
m_notification->sendEvent();
}
QString AlarmNotification::uid() const
{
return m_uid;
}
QString AlarmNotification::text() const
{
return m_notification->text();
}
void AlarmNotification::setText(const QString &alarmText)
{
m_notification->setText(alarmText);
}
QDateTime AlarmNotification::remindAt() const
{
return m_remind_at;
}
void AlarmNotification::setRemindAt(const QDateTime &remindAtDt)
{
m_remind_at = remindAtDt;
}
/*
* SPDX-FileCopyrightText: 2019 Dimitris Kardarakos <dimkard@posteo.net>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <KNotification>
#include <QDateTime>
class NotificationHandler;
/**
* @brief The alarm notification that should be displayed. It is a wrapper of a KNotification enhanced with alarm properties, like uid and remind time
*
*/
class AlarmNotification : public QObject
{
Q_OBJECT
public:
explicit AlarmNotification(NotificationHandler *handler, const QString &uid);
~AlarmNotification() override;
/**
* @brief Sends the notification to be displayed
*/
void send() const;
/**
* @return The uid of the Incidence of the alarm of the notification
*/
QString uid() const;
/**
* @brief The text of the notification that should be displayed
*/
QString text() const;
/**
* @brief Sets the to-be-displayed text of the notification
*/
void setText(const QString &alarmText);
/**
* @return In case of a suspended notification, the time that the notification should be displayed. Otherwise, it is empty.
*/
QDateTime remindAt() const;
/**
* @brief Sets the time that should be displayed a suspended notification
*/
void setRemindAt(const QDateTime &remindAtDt);
Q_SIGNALS:
/**
* @brief Signal that should be emitted when the user clicks to the Dismiss action button of the KNotification displayed
*/
void dismiss();
/**
* @brief Signal that should be emitted when the user clicks to the Suspend action button of the KNotification displayed
*/
void suspend();
private:
KNotification *m_notification;
QString m_uid;
QDateTime m_remind_at;
NotificationHandler *m_notification_handler;
};
[Global]
IconName=org.kde.kalendar
DesktopEntry=org.kde.kalendar
Comment=Manage your tasks and events with Kalendar
Comment[ca]=Gestioneu les tasques i esdeveniments amb el Kalendar
Comment[ca@valencia]=Gestioneu les tasques i esdeveniments amb el Kalendar
Comment[el]=Διαχείριση εργασιών και γεγονότων με το Kalendar
Comment[en_GB]=Manage your tasks and events with Kalendar
Comment[es]=Gestione tareas y eventos con Kalendar
Comment[fr]=Gérer vos tâches et vos évènements avec Kalendar
Comment[it]=Gestisci le tue attività e gli eventi con Kalendar
Comment[nl]=Uw taken en afspraken beheren met Kalendar
Comment[pl]=Zarządzaj swoimi zadaniami i wydarzeniami z Kalendarzem
Comment[pt]=Gerir as suas tarefas e eventos com o Kalendar
Comment[pt_BR]=Gerencie suas tarefas e eventos com o Kalendar
Comment[sl]=Upravljajte vaše naloge in dogodke s programom Kalendar
Comment[sv]=Hantera dina uppgifter och händelser med Kalendar
Comment[uk]=Керуйте вашими записами завдань і подій за допомогою Kalendar
Comment[x-test]=xxManage your tasks and events with Kalendarxx
Name=Kalendar
Name[ca]=Kalendar
Name[ca@valencia]=Kalendar
Name[cs]=Kalendar
Name[de]=Kalendar
Name[el]=Kalendar
Name[en_GB]=Kalendar
Name[es]=Kalendar
Name[fi]=Kalendar
Name[fr]=Kalendar
Name[it]=Kalendar
Name[ko]=Kalendar
Name[nl]=Kalendar
Name[pl]=Kalendarz
Name[pt]=Kalendar
Name[pt_BR]=Kalendar
Name[sl]=Kalendar
Name[sv]=Kalendar
Name[uk]=Kalendar
Name[x-test]=xxKalendarxx
Name[zh_CN]=Kalendar
[Context/uid]
Name=Incidence uid
Name[ca]=UID de la incidència
Name[ca@valencia]=UID de la incidència
Name[de]=Ereigniskennung
Name[el]=Κωδικός περιστατικού
Name[en_GB]=Incidence UID
Name[es]=UID de la incidencia
Name[et]=Sündmuse UID
Name[eu]=Intzidentziaren UID
Name[fi]=Esiintymän UID
Name[fr]=UID de l'incidence
Name[gl]=Identificador único de incidencia
Name[ia]=uid de incidentia
Name[it]=UID incidenza
Name[ko]=일정 UID
Name[nl]=Uid van incident
Name[nn]=Hendings-UID
Name[pa]=ਘਟਨਾ uid
Name[pl]=UID wystąpienia
Name[pt]=UID da incidência
Name[pt_BR]=UID da incidência
Name[sv]=Förekomstens användar-id
Name[uk]=Ідентифікатор випадку
Name[x-test]=xxIncidence uidxx
Name[zh_CN]=事件 UID
Comment=The uid of the incidence of the alarm
Comment[ca]=L'UID de la incidència de l'alarma
Comment[ca@valencia]=L'UID de la incidència de l'alarma
Comment[de]=Die Ereigniskennung der Erinnerung
Comment[el]=Ο κωδικός του περιστατικού για το συναγερμό
Comment[en_GB]=The UID of the incidence of the alarm
Comment[es]=El UID de la incidencia de la alarma
Comment[et]=Häiresündmuse UID
Comment[eu]=Alarma-intzidentziaren UID
Comment[fi]=Hälytysesiintymän UID
Comment[fr]=L'UID de l'incidence de l'alarme
Comment[gl]=O identificador único da incidencia da alarma
Comment[ia]=Le uid del incidentia del alarma
Comment[it]=L'UID dell'incidenza dell'allarme
Comment[ko]=알람 일정의 UID
Comment[nl]=De uid van het incident van het alarm
Comment[nn]=UID-en til alarm­hendinga
Comment[pa]=ਅਲਾਰਮ ਦੀ ਘਟਨਾ ਦਾ uid
Comment[pl]=UID wystąpienia alarmu
Comment[pt]=O UID da incidência do alarme
Comment[pt_BR]=A UID da incidência do alarme
Comment[sv]=Användar-id för förekomsten av alarmet
Comment[uk]=Ідентифікатор випадку нагадування
Comment[x-test]=xxThe uid of the incidence of the alarmxx
Comment[zh_CN]=提醒事件的 UID
[Event/alarm]
Name=Alarm
Name[ca]=Alarma
Name[ca@valencia]=Alarma
Name[cs]=Alarm
Name[de]=Erinnerung
Name[el]=Συναγερμός
Name[en_GB]=Alarm
Name[es]=Alarma
Name[et]=Häire
Name[eu]=Alarma
Name[fi]=Hälytys
Name[fr]=Alarme
Name[gl]=Alarma
Name[ia]=Alarma
Name[it]=Allarme
Name[ko]=알람
Name[nl]=Alarm
Name[nn]=Alarm
Name[pa]=ਅਲਾਰਮ
Name[pl]=Alarm
Name[pt]=Alarme
Name[pt_BR]=Alarme
Name[sk]=Alarm
Name[sl]=Alarm
Name[sv]=Alarm
Name[uk]=Нагадування
Name[x-test]=xxAlarmxx
Name[zh_CN]=提醒
Name[zh_TW]=鬧鐘
Contexts=uid
Comment=Alarm Notification
Comment[ca]=Notificació de l'alarma
Comment[ca@valencia]=Notificació de l'alarma
Comment[de]=Erinnerungs-Benachrichtigung
Comment[el]=Ειδοποίηση συναγερμού
Comment[en_GB]=Alarm Notification
Comment[es]=Notificación de alarma
Comment[et]=Häire märguanne
Comment[eu]=Alarma jakinarazpena
Comment[fi]=Hälytysilmoitus
Comment[fr]=Notification d'alarme
Comment[gl]=Notificación de alarma
Comment[ia]=Notification de alarma
Comment[it]=Notifica dell'allarme
Comment[ko]=알람 알림
Comment[nl]=Alarmmelding
Comment[nn]=Alarm­varsling
Comment[pa]=ਅਲਾਰਮ ਨੋਟੀਫਿਕੇਸ਼ਨ
Comment[pl]=Powiadomienie o alarmie
Comment[pt]=Notificação do Alarme
Comment[pt_BR]=Notificação de alarme
Comment[sl]=Obvestilo o alarmu
Comment[sv]=Alarmunderrättelse
Comment[uk]=Сповіщення для нагадування
Comment[x-test]=xxAlarm Notificationxx
Comment[zh_CN]=提醒通知
Comment[zh_TW]=鬧鐘通知
Action=Popup|Sound
Sound=freedesktop/stereo/bell.oga
Urgency=Critical
# SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
# SPDX-License-Identifier: CC0-1.0
// SPDX-FileCopyrightText: 2021 Claudio Cambra <claudio.cambra@gmail.com>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "kalendaralarmclient.h"
#include <KAboutData>
#include <KDBusService>
#include <KLocalizedString>
#include <QApplication>
#include <QCommandLineParser>
int main(int argc, char **argv)
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
KAboutData aboutData(
// The program name used internally.
QStringLiteral("kalendarac"),
// A displayable program name string.
i18nc("@title", "Kalendar Reminders"),
QStringLiteral(),