File indexing completed on 2023-11-26 08:16:45

0001 /*
0002  * Presence Model - A model of settable presences.
0003  *
0004  * Copyright (C) 2016 James D. Smith <smithjd15@gmail.com>
0005  * Copyright (C) 2011 David Edmundson <kde@davidedmundson.co.uk>
0006  *
0007  * This library is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU Lesser General Public
0009  * License as published by the Free Software Foundation; either
0010  * version 2.1 of the License, or (at your option) any later version.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Lesser General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Lesser General Public
0018  * License along with this library; if not, write to the Free Software
0019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0020  */
0021 
0022 #include "presence-model.h"
0023 
0024 #include <QString>
0025 #include <QFont>
0026 #include <QFontDatabase>
0027 #include <QIcon>
0028 #include <QtDBus/QtDBus>
0029 #include <QVariant>
0030 
0031 #include <KSharedConfig>
0032 #include <KLocalizedString>
0033 #include <KConfig>
0034 #include <KConfigGroup>
0035 
0036 #include <KTp/presence.h>
0037 
0038 #include <functional>
0039 
0040 #include "types.h"
0041 #include "debug.h"
0042 
0043 namespace KTp
0044 {
0045 
0046 PresenceModel::PresenceModel(QObject *parent) :
0047     QAbstractListModel(parent)
0048 {
0049     Tp::registerTypes();
0050 
0051     loadPresences();
0052 
0053     QDBusConnection::sessionBus().connect(QString(), QLatin1String("/Telepathy"),
0054                                               QLatin1String("org.kde.Telepathy"),
0055                                               QLatin1String("presenceModelChanged"),
0056                                               this,
0057                                               SLOT(propagationChange(QVariantList)));
0058 }
0059 
0060 PresenceModel::~PresenceModel()
0061 {
0062 }
0063 
0064 void PresenceModel::syncCustomPresencesToDisk()
0065 {
0066     m_presenceGroup.deleteGroup();
0067 
0068     Q_FOREACH (const KTp::Presence &presence, m_presences) {
0069         if (!presence.statusMessage().isEmpty()) {
0070             QVariantList presenceVariant;
0071             presenceVariant.append(presence.type());
0072             presenceVariant.append(presence.statusMessage());
0073             QString id = QString::number(presence.type()).append(presence.statusMessage());
0074             m_presenceGroup.writeEntry(id, presenceVariant);
0075         }
0076     }
0077     m_presenceGroup.sync();
0078 }
0079 
0080 void PresenceModel::propagationChange(const QVariantList modelChange)
0081 {
0082     KTp::Presence presence = KTp::Presence(qdbus_cast<Tp::SimplePresence>(modelChange.value(0)));
0083     bool presenceAdded = qdbus_cast<bool>(modelChange.value(1));
0084 
0085     if (!presence.isValid()) {
0086         return;
0087     }
0088 
0089     if (presenceAdded != m_presences.contains(presence)) {
0090         modifyModel(presence);
0091     }
0092 }
0093 
0094 int PresenceModel::rowCount(const QModelIndex &parent) const
0095 {
0096     Q_UNUSED(parent)
0097     return m_presences.size();
0098 }
0099 
0100 QVariant PresenceModel::data(const QModelIndex &index, int role) const
0101 {
0102     if (!index.isValid()) {
0103         qCDebug(KTP_MODELS) << "invalid index data requested" << index;
0104         return QVariant();
0105     }
0106 
0107     KTp::Presence presence = m_presences[index.row()];
0108     switch (role) {
0109     case Qt::DisplayRole:
0110         if (presence.statusMessage().isEmpty()) {
0111             return QVariant(presence.displayString());
0112         } else {
0113             return QVariant(presence.statusMessage());
0114         }
0115 
0116     case Qt::DecorationRole:
0117         return QVariant(presence.icon());
0118 
0119     case Qt::FontRole:
0120         if (presence.statusMessage().isEmpty()) {
0121             QFont font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
0122             font.setBold(true);
0123             return font;
0124         } else {
0125             return QVariant();
0126         }
0127 
0128     case PresenceRole:
0129         return QVariant::fromValue<KTp::Presence>(presence);
0130 
0131     case IconNameRole:
0132         return QVariant(presence.iconName());
0133     }
0134 
0135     return QVariant();
0136 }
0137 
0138 QModelIndexList PresenceModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
0139 {
0140     Q_UNUSED(flags);
0141 
0142     QModelIndexList items;
0143 
0144     for (int i = 0; i < m_presences.size(); i++) {
0145         if (i < start.row())
0146             continue;
0147 
0148         const KTp::Presence &presence = m_presences[i];
0149         if (role == Qt::DisplayRole) {
0150             if (presence.statusMessage().isEmpty()
0151               && (presence.displayString() == qvariant_cast<QString>(value))) {
0152                 items.append(createIndex(i, 0));
0153             } else if (presence.statusMessage() == qvariant_cast<QString>(value)) {
0154                 items.append(createIndex(i, 0));
0155             }
0156         }
0157         if (role == PresenceRole) {
0158             const KTp::Presence &matchPresence = qvariant_cast<KTp::Presence>(value);
0159             if (presence == matchPresence) {
0160                 items.append(createIndex(i, 0));
0161             }
0162         }
0163 
0164         if (items.size() == hits) {
0165             return items;
0166         }
0167     }
0168 
0169     return items;
0170 }
0171 
0172 void PresenceModel::loadPresences()
0173 {
0174     KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("ktelepathyrc"));
0175     config->reparseConfiguration();
0176     m_presenceGroup = config->group("Custom Presence List");
0177     m_presences.clear();
0178     loadDefaultPresences();
0179     loadCustomPresences();
0180 }
0181 
0182 void PresenceModel::loadDefaultPresences()
0183 {
0184     modifyModel(KTp::Presence::available());
0185     modifyModel(KTp::Presence::busy());
0186     modifyModel(KTp::Presence::away());
0187     modifyModel(KTp::Presence::xa());
0188     modifyModel(KTp::Presence::hidden());
0189     modifyModel(KTp::Presence::offline());
0190 }
0191 
0192 void PresenceModel::loadCustomPresences()
0193 {
0194     Q_FOREACH (const QString &key, m_presenceGroup.keyList()) {
0195         QVariantList entry = m_presenceGroup.readEntry(key, QVariantList());
0196 
0197         QString statusMessage = entry.last().toString();
0198 
0199         switch (entry.first().toInt()) {
0200         case Tp::ConnectionPresenceTypeAvailable:
0201             modifyModel(KTp::Presence::available(statusMessage));
0202             break;
0203         case Tp::ConnectionPresenceTypeAway:
0204             modifyModel(KTp::Presence::away(statusMessage));
0205             break;
0206         case Tp::ConnectionPresenceTypeBusy:
0207             modifyModel(KTp::Presence::busy(statusMessage));
0208             break;
0209         case Tp::ConnectionPresenceTypeExtendedAway:
0210             modifyModel(KTp::Presence::xa(statusMessage));
0211         }
0212     }
0213 }
0214 
0215 void PresenceModel::modifyModel(const KTp::Presence &presence)
0216 {
0217     if (m_presences.contains(presence)) {
0218         int row = m_presences.indexOf(presence);
0219         beginRemoveRows(QModelIndex(), row, row);
0220         m_presences.removeAt(row);
0221         endRemoveRows();
0222     } else {
0223         // Identical presence types with status messages are compared with the
0224         // status messages in ascending order.
0225         auto presenceMessageGreaterThan = [] (const KTp::Presence &presence, const KTp::Presence &other) {
0226             if (KTp::Presence::sortPriority(presence.type()) == KTp::Presence::sortPriority(other.type())) {
0227                 return (QString::localeAwareCompare(presence.statusMessage(), other.statusMessage()) < 0);
0228             } else {
0229                 return (KTp::Presence::sortPriority(presence.type()) < KTp::Presence::sortPriority(other.type()));
0230             }
0231         };
0232 
0233         int row = std::lower_bound(m_presences.constBegin(), m_presences.constEnd(), presence, presenceMessageGreaterThan) - m_presences.constBegin();
0234 
0235         beginInsertRows(QModelIndex(), row, row);
0236         m_presences.insert(row, presence);
0237         endInsertRows();
0238     }
0239 }
0240 
0241 QModelIndex PresenceModel::addPresence(const KTp::Presence &presence)
0242 {
0243     if (!m_presences.contains(presence)) {
0244         modifyModel(presence);
0245         propagateChange(presence);
0246     }
0247 
0248     return createIndex(m_presences.indexOf(presence), 0);
0249 }
0250 
0251 void PresenceModel::removePresence(const KTp::Presence &presence)
0252 {
0253     if (m_presences.contains(presence)) {
0254         modifyModel(presence);
0255         propagateChange(presence);
0256     }
0257 }
0258 
0259 void PresenceModel::propagateChange(const KTp::Presence &presence)
0260 {
0261     QVariantList messageArgList;
0262     QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/Telepathy"),
0263                                                           QLatin1String("org.kde.Telepathy"),
0264                                                           QLatin1String("presenceModelChanged"));
0265 
0266     messageArgList << QVariant::fromValue<Tp::SimplePresence>(presence.barePresence());
0267     messageArgList << QVariant::fromValue<bool>(m_presences.contains(presence));
0268     message << messageArgList;
0269 
0270     if (!QDBusConnection::sessionBus().send(message)) {
0271         const QString &error = QDBusConnection::sessionBus().lastError().message();
0272         qCWarning(KTP_MODELS) << "presence model change propagation failed: " << error;
0273     }
0274 }
0275 
0276 QHash<int, QByteArray> PresenceModel::roleNames() const
0277 {
0278     QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
0279     roles.insert(PresenceRole, "presence");
0280     roles.insert(IconNameRole, "iconName");
0281     return roles;
0282 }
0283 
0284 QVariant PresenceModel::get(int row, const QByteArray& role) const
0285 {
0286     //TODO: cache roles?
0287     QHash<int, QByteArray> roles = roleNames();
0288     return index(row, 0).data(roles.key(role));
0289 }
0290 
0291 }