File indexing completed on 2024-06-23 05:45:41
0001 /* 0002 * Copyright 2020 Han Young <hanyoung@protonmail.com> 0003 * Copyright 2020-2021 Devin Lin <devin@kde.org> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "alarmmodel.h" 0009 0010 #include "kclock_algorithm.hpp" 0011 #include "utilmodel.h" 0012 0013 #include <KConfigGroup> 0014 #include <KLocalizedString> 0015 #include <KSharedConfig> 0016 #include <KStatusNotifierItem> 0017 0018 #include <QDBusConnection> 0019 #include <QDBusInterface> 0020 #include <QDBusReply> 0021 #include <QLocale> 0022 #include <QThread> 0023 #include <QXmlStreamReader> 0024 0025 AlarmModel *AlarmModel::instance() 0026 { 0027 static AlarmModel *singleton = new AlarmModel(); 0028 return singleton; 0029 } 0030 0031 AlarmModel::AlarmModel(QObject *parent) 0032 : QAbstractListModel{parent} 0033 , m_interface{new org::kde::kclock::AlarmModel(QStringLiteral("org.kde.kclockd"), QStringLiteral("/Alarms"), QDBusConnection::sessionBus(), this)} 0034 { 0035 if (m_interface->isValid()) { 0036 connect(m_interface, SIGNAL(alarmAdded(QString)), this, SLOT(addAlarmInternal(QString))); 0037 connect(m_interface, SIGNAL(alarmRemoved(QString)), this, SLOT(removeAlarm(QString))); 0038 } 0039 setConnectedToDaemon(m_interface->isValid()); 0040 load(); 0041 0042 // watch for kclockd 0043 m_watcher = new QDBusServiceWatcher(QStringLiteral("org.kde.kclockd"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); 0044 connect(m_watcher, &QDBusServiceWatcher::serviceRegistered, this, [this]() -> void { 0045 setConnectedToDaemon(true); 0046 if (m_interface->isValid()) { 0047 connect(m_interface, SIGNAL(alarmAdded(QString)), this, SLOT(addAlarmInternal(QString))); 0048 connect(m_interface, SIGNAL(alarmRemoved(QString)), this, SLOT(removeAlarm(QString))); 0049 } 0050 }); 0051 connect(m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, [this]() -> void { 0052 setConnectedToDaemon(false); 0053 }); 0054 } 0055 0056 void AlarmModel::load() 0057 { 0058 // load from dbus 0059 QDBusInterface *interface = new QDBusInterface(QStringLiteral("org.kde.kclockd"), 0060 QStringLiteral("/Alarms"), 0061 QStringLiteral("org.freedesktop.DBus.Introspectable"), 0062 QDBusConnection::sessionBus(), 0063 this); 0064 QDBusReply<QString> reply = interface->call(QStringLiteral("Introspect")); 0065 if (reply.isValid()) { 0066 QXmlStreamReader xml(reply.value()); 0067 while (!xml.atEnd()) { 0068 xml.readNext(); 0069 if (xml.name() == QStringLiteral("node") && xml.attributes().hasAttribute(QStringLiteral("name"))) { 0070 if (xml.attributes().value(QStringLiteral("name")).toString().indexOf(QStringLiteral("org")) == -1) { 0071 this->addAlarmInternal(xml.attributes().value(QStringLiteral("name")).toString()); 0072 } 0073 } 0074 } 0075 } 0076 0077 interface->deleteLater(); 0078 } 0079 0080 QHash<int, QByteArray> AlarmModel::roleNames() const 0081 { 0082 return {{AlarmRole, "alarm"}}; 0083 } 0084 0085 QVariant AlarmModel::data(const QModelIndex &index, int role) const 0086 { 0087 if (!index.isValid() || index.row() >= alarmsList.count()) { 0088 return QVariant(); 0089 } 0090 0091 auto *alarm = alarmsList[index.row()]; 0092 return alarm ? QVariant::fromValue(alarm) : QVariant(); 0093 } 0094 0095 int AlarmModel::rowCount(const QModelIndex &parent) const 0096 { 0097 Q_UNUSED(parent); 0098 return alarmsList.size(); 0099 } 0100 0101 Qt::ItemFlags AlarmModel::flags(const QModelIndex &index) const 0102 { 0103 Q_UNUSED(index); 0104 return Qt::ItemIsEditable; 0105 } 0106 0107 void AlarmModel::remove(int index) 0108 { 0109 if (index < 0 || index >= this->alarmsList.size()) 0110 return; 0111 0112 m_interface->removeAlarm(alarmsList.at(index)->uuid().toString()); 0113 auto ptr = alarmsList.at(index); 0114 0115 Q_EMIT beginRemoveRows(QModelIndex(), index, index); 0116 alarmsList.removeAt(index); 0117 Q_EMIT endRemoveRows(); 0118 0119 ptr->deleteLater(); 0120 } 0121 0122 void AlarmModel::addAlarm(const QString &name, int hours, int minutes, int daysOfWeek, const QString &audioPath, int ringDuration, int snoozeDuration) 0123 { 0124 m_interface->addAlarm(name, hours, minutes, daysOfWeek, audioPath, ringDuration, snoozeDuration); 0125 } 0126 0127 QString AlarmModel::timeToRingFormatted(int hours, int minutes, int daysOfWeek) 0128 { 0129 return UtilModel::instance()->timeToRingFormatted(UtilModel::instance()->calculateNextRingTime(hours, minutes, daysOfWeek)); 0130 } 0131 0132 void AlarmModel::addAlarmInternal(QString uuid) 0133 { 0134 auto alarm = new Alarm(uuid.remove(QRegularExpression(QStringLiteral("[{}-]")))); 0135 auto index = KClock::insert_index(alarm, alarmsList, [](Alarm *const &left, Alarm *const &right) { 0136 if (left->hours() < right->hours()) { 0137 return true; 0138 } else if (left->hours() > right->hours()) { 0139 return false; 0140 } else if (left->minutes() <= right->minutes()) { 0141 return true; 0142 } else { 0143 return false; 0144 } 0145 }); 0146 0147 Q_EMIT beginInsertRows(QModelIndex(), index, index); 0148 alarmsList.insert(index, alarm); 0149 Q_EMIT endInsertRows(); 0150 } 0151 0152 void AlarmModel::removeAlarm(const QString &uuid) 0153 { 0154 auto index = 0; 0155 for (auto alarm : alarmsList) { 0156 if (alarm->uuid().toString() == uuid) { 0157 break; 0158 } 0159 ++index; 0160 } 0161 0162 if (index >= this->alarmsList.size()) 0163 return; 0164 0165 auto ptr = alarmsList.at(index); 0166 0167 Q_EMIT beginRemoveRows(QModelIndex(), index, index); 0168 alarmsList.removeAt(index); 0169 Q_EMIT endRemoveRows(); 0170 0171 ptr->deleteLater(); 0172 } 0173 0174 bool AlarmModel::connectedToDaemon() 0175 { 0176 return m_connectedToDaemon; 0177 } 0178 0179 void AlarmModel::setConnectedToDaemon(bool connectedToDaemon) 0180 { 0181 if (m_connectedToDaemon != connectedToDaemon) { 0182 m_connectedToDaemon = connectedToDaemon; 0183 Q_EMIT connectedToDaemonChanged(); 0184 } 0185 }