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 }