File indexing completed on 2023-11-26 08:16:43
0001 /* 0002 * This file is part of ktp-common-internals 0003 * 0004 * Copyright (C) 2009 Collabora Ltd. <info@collabora.com> 0005 * Copyright (C) 2012 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 "accounts-list-model.h" 0023 0024 #include <QIcon> 0025 #include <QDBusInterface> 0026 #include <QDBusPendingCall> 0027 #include <QVariant> 0028 0029 #include "debug.h" 0030 #include "types.h" 0031 0032 #include <KLocalizedString> 0033 #include <KPixmapSequence> 0034 0035 #include <KTp/error-dictionary.h> 0036 #include <KTp/presence.h> 0037 0038 #include <TelepathyQt/Account> 0039 #include <TelepathyQt/AccountSet> 0040 #include <TelepathyQt/Constants> 0041 #include <TelepathyQt/Presence> 0042 0043 class KTp::AccountsListModel::Private : public QObject { 0044 Q_OBJECT 0045 public: 0046 QList<Tp::AccountPtr> accounts; 0047 Tp::AccountSetPtr accountSet; 0048 QDBusInterface *statusHandler; 0049 0050 Q_SIGNALS: 0051 void statusHandlerStatusChange(const QString &accountUID); 0052 }; 0053 0054 0055 KTp::AccountsListModel::AccountsListModel(QObject *parent) 0056 : QAbstractListModel(parent), 0057 d(new AccountsListModel::Private) 0058 { 0059 Tp::registerTypes(); 0060 0061 d->statusHandler = new QDBusInterface(QLatin1String("org.freedesktop.Telepathy.Client.KTp.KdedIntegrationModule"), 0062 QLatin1String("/StatusHandler"), 0063 QString(), 0064 QDBusConnection::sessionBus(), this); 0065 0066 connect(d->statusHandler, SIGNAL(statusChange(QString)), d, SIGNAL(statusHandlerStatusChange(QString))); 0067 } 0068 0069 KTp::AccountsListModel::~AccountsListModel() 0070 { 0071 delete d; 0072 } 0073 0074 QHash<int, QByteArray> KTp::AccountsListModel::roleNames() const 0075 { 0076 QHash<int, QByteArray> roles = QAbstractListModel::roleNames(); 0077 roles[ConnectionStateRole] = "connectionState"; 0078 roles[ConnectionStateDisplayRole] = "conectionStateDisplay"; 0079 roles[ConnectionStateIconRole] = "connectionStateIcon"; 0080 roles[ConnectionErrorMessageDisplayRole] = "connectionErrorMessage"; 0081 roles[ConnectionProtocolNameRole] = "connectionProtocolName"; 0082 roles[StatusHandlerSessionPresenceRole] = "statusHandlerSessionPresence"; 0083 roles[StatusHandlerPresenceRole] = "statusHandlerPresence"; 0084 roles[RequestedPresenceRole] = "requestedPresence"; 0085 roles[IconNameRole] = "iconName"; 0086 roles[EnabledRole] = "enabled"; 0087 roles[AccountRole] = "account"; 0088 return roles; 0089 } 0090 0091 void KTp::AccountsListModel::setAccountSet(const Tp::AccountSetPtr &accountSet) 0092 { 0093 beginResetModel(); 0094 d->accounts.clear(); 0095 endResetModel(); 0096 0097 d->accountSet = accountSet; 0098 Q_FOREACH(const Tp::AccountPtr &account, d->accountSet->accounts()) { 0099 onAccountAdded(account); 0100 } 0101 0102 connect(d->accountSet.data(), SIGNAL(accountAdded(Tp::AccountPtr)), SLOT(onAccountAdded(Tp::AccountPtr))); 0103 connect(d->accountSet.data(), SIGNAL(accountRemoved(Tp::AccountPtr)), SLOT(onAccountRemoved(Tp::AccountPtr))); 0104 } 0105 0106 int KTp::AccountsListModel::rowCount(const QModelIndex &parent) const 0107 { 0108 // If the index is the root item, then return the row count. 0109 if (parent == QModelIndex()) { 0110 return d->accounts.size(); 0111 } 0112 0113 // Otherwise, return 0 (as this is a list model, so all items 0114 // are children of the root item). 0115 return 0; 0116 } 0117 0118 QVariant KTp::AccountsListModel::data(const QModelIndex &index, int role) const 0119 { 0120 if (!index.isValid()) { 0121 return QVariant(); 0122 } 0123 0124 QVariant data; 0125 Tp::AccountPtr account = d->accounts.at(index.row()); 0126 0127 switch (role) { 0128 case Qt::DisplayRole: 0129 data = QVariant(account->displayName()); 0130 break; 0131 0132 case Qt::DecorationRole: 0133 data = QVariant(QIcon::fromTheme(account->iconName())); 0134 break; 0135 0136 case AccountsListModel::ConnectionStateRole: 0137 data = QVariant(account->connectionStatus()); 0138 break; 0139 0140 case AccountsListModel::ConnectionStateDisplayRole: 0141 data = QVariant(connectionStateString(account)); 0142 break; 0143 0144 case AccountsListModel::ConnectionStateIconRole: 0145 data = QVariant(connectionStateIcon(account)); 0146 break; 0147 0148 case AccountsListModel::ConnectionErrorMessageDisplayRole: 0149 data = QVariant(connectionStatusReason(account)); 0150 break; 0151 0152 case AccountsListModel::ConnectionProtocolNameRole: 0153 data = QVariant(account->protocolName()); 0154 break; 0155 0156 case AccountsListModel::StatusHandlerSessionPresenceRole: 0157 case AccountsListModel::StatusHandlerPresenceRole: 0158 if (d->statusHandler->property("requestedAccountPresences").toHash().contains(account->uniqueIdentifier())) { 0159 data = QVariant::fromValue<KTp::Presence>(KTp::Presence(qdbus_cast<Tp::SimplePresence>(d->statusHandler->property("requestedAccountPresences").toHash().value(account->uniqueIdentifier())))); 0160 } else { 0161 data = QVariant::fromValue<KTp::Presence>(Tp::Presence(Tp::ConnectionPresenceTypeUnset, QLatin1String("unset"), QString())); 0162 } 0163 break; 0164 0165 case AccountsListModel::RequestedPresenceRole: 0166 data = QVariant::fromValue<KTp::Presence>(account->requestedPresence()); 0167 break; 0168 0169 case AccountsListModel::IconNameRole: 0170 data = QVariant(account->iconName()); 0171 break; 0172 0173 case AccountsListModel::EnabledRole: 0174 if (account->isEnabled()) { 0175 data = QVariant(Qt::Checked); 0176 } else { 0177 data = QVariant(Qt::Unchecked); 0178 } 0179 break; 0180 0181 case AccountsListModel::AccountRole: 0182 data = QVariant::fromValue<Tp::AccountPtr>(account); 0183 break; 0184 0185 default: 0186 break; 0187 } 0188 0189 return data; 0190 } 0191 0192 bool KTp::AccountsListModel::setData(const QModelIndex &index, const QVariant &value, int role) 0193 { 0194 if (!index.isValid()) { 0195 return false; 0196 } 0197 if ((role == AccountsListModel::StatusHandlerSessionPresenceRole) || (role == AccountsListModel::StatusHandlerPresenceRole)) { 0198 const QVariant &accountUID = QVariant::fromValue<QString>(index.data(AccountRole).value<Tp::AccountPtr>()->uniqueIdentifier()); 0199 const QVariant &presence = QVariant::fromValue<Tp::SimplePresence>(qvariant_cast<KTp::Presence>(value).barePresence()); 0200 QVariant presenceClass; 0201 if (role == AccountsListModel::StatusHandlerPresenceRole) { 0202 presenceClass = QVariant::fromValue<uint>(0); 0203 } else { 0204 presenceClass = QVariant::fromValue<uint>(1); 0205 } 0206 0207 QDBusPendingCall call = d->statusHandler->asyncCall(QLatin1String("setRequestedAccountPresence"), accountUID, presence, presenceClass); 0208 0209 return true; 0210 } 0211 if (role == AccountsListModel::RequestedPresenceRole) { 0212 index.data(AccountRole).value<Tp::AccountPtr>()->setRequestedPresence(qvariant_cast<KTp::Presence>(value)); 0213 return true; 0214 } 0215 if (role == AccountsListModel::EnabledRole) { 0216 //this is index from QSortFilterProxyModel 0217 index.data(AccountRole).value<Tp::AccountPtr>()->setEnabled(value.toInt() == Qt::Checked); 0218 return true; 0219 } 0220 0221 return false; 0222 } 0223 0224 void KTp::AccountsListModel::onAccountAdded(const Tp::AccountPtr &account) 0225 { 0226 qCDebug(KTP_MODELS) << "Creating a new Account from account:" << account.data(); 0227 0228 // Check if the account is already in the model. 0229 bool found = false; 0230 0231 if (!found) { 0232 Q_FOREACH (const Tp::AccountPtr &ai, d->accounts) { 0233 if (ai == account) { 0234 found = true; 0235 break; 0236 } 0237 } 0238 } 0239 0240 if (found) { 0241 qCWarning(KTP_MODELS) << "Requested to add account" 0242 << account.data() 0243 << "to model, but it is already present. Doing nothing."; 0244 } else { 0245 qCDebug(KTP_MODELS) << "Account not already in model. Create new Account from account:" 0246 << account.data(); 0247 0248 auto accountIdentifiersLessThan = [] (const Tp::AccountPtr &account, const Tp::AccountPtr &other) { 0249 if (account->serviceName() == other->serviceName()) { 0250 return (QString::localeAwareCompare(account->normalizedName(), other->normalizedName()) < 0); 0251 } else { 0252 return (QString::localeAwareCompare(account->serviceName(), other->serviceName()) < 0); 0253 } 0254 }; 0255 0256 int row = std::lower_bound(d->accounts.constBegin(), d->accounts.constEnd(), account, accountIdentifiersLessThan) - d->accounts.constBegin(); 0257 beginInsertRows(QModelIndex(), row, row); 0258 d->accounts.insert(row, account); 0259 endInsertRows(); 0260 0261 connect(account.data(), 0262 SIGNAL(stateChanged(bool)), 0263 SLOT(onAccountUpdated())); 0264 connect(account.data(), 0265 SIGNAL(displayNameChanged(QString)), 0266 SLOT(onAccountUpdated())); 0267 connect(account.data(), 0268 SIGNAL(connectionStatusChanged(Tp::ConnectionStatus)), 0269 SLOT(onAccountUpdated())); 0270 connect(account.data(), 0271 SIGNAL(currentPresenceChanged(Tp::Presence)), 0272 SLOT(onAccountUpdated())); 0273 connect(account.data(), 0274 SIGNAL(iconNameChanged(QString)), 0275 SLOT(onAccountUpdated())); 0276 connect(account.data(), 0277 SIGNAL(stateChanged(bool)), 0278 SLOT(onAccountUpdated())); 0279 connect(d, &AccountsListModel::Private::statusHandlerStatusChange, [=] (const QString &accountUID) { 0280 if (accountUID == account->uniqueIdentifier()) { 0281 onAccountUpdated(); 0282 } 0283 }); 0284 } 0285 } 0286 0287 void KTp::AccountsListModel::onAccountRemoved(const Tp::AccountPtr &account) 0288 { 0289 int row = d->accounts.indexOf(account); 0290 beginRemoveRows(QModelIndex(), row, row); 0291 d->accounts.removeAt(row); 0292 endRemoveRows(); 0293 } 0294 0295 void KTp::AccountsListModel::onAccountUpdated() 0296 { 0297 Tp::AccountPtr item = Tp::AccountPtr(qobject_cast<Tp::Account*>(sender())); 0298 0299 Q_ASSERT(item); 0300 if (!item) { 0301 qCWarning(KTP_MODELS) << "Not an Account pointer:" << sender(); 0302 return; 0303 } 0304 0305 QModelIndex index = createIndex(d->accounts.lastIndexOf(item), 0); 0306 Q_EMIT dataChanged(index, index); 0307 } 0308 0309 const QString KTp::AccountsListModel::connectionStateString(const Tp::AccountPtr &account) const 0310 { 0311 if (account->isEnabled()) { 0312 switch (account->connectionStatus()) { 0313 case Tp::ConnectionStatusConnected: 0314 return KTp::Presence(account->currentPresence()).displayString(); 0315 case Tp::ConnectionStatusConnecting: 0316 return i18nc("This is a connection state", "Connecting"); 0317 case Tp::ConnectionStatusDisconnected: 0318 return i18nc("This is a connection state", "Disconnected"); 0319 default: 0320 return i18nc("This is an unknown connection state", "Unknown"); 0321 } 0322 } else { 0323 return i18nc("This is a disabled account", "Disabled"); 0324 } 0325 } 0326 0327 const QIcon KTp::AccountsListModel::connectionStateIcon(const Tp::AccountPtr &account) const 0328 { 0329 if (account->isEnabled()) { 0330 switch (account->connectionStatus()) { 0331 case Tp::ConnectionStatusConnected: 0332 return KTp::Presence(account->currentPresence()).icon(); 0333 case Tp::ConnectionStatusConnecting: 0334 //imho this is not really worth animating, but feel free to play around.. 0335 return QIcon(KPixmapSequence(QLatin1String("process-working"), 22).frameAt(0)); 0336 case Tp::ConnectionStatusDisconnected: 0337 return QIcon::fromTheme(QStringLiteral("user-offline")); 0338 default: 0339 return QIcon::fromTheme(QStringLiteral("user-offline")); 0340 } 0341 } else { 0342 return QIcon(); 0343 } 0344 } 0345 0346 const QString KTp::AccountsListModel::connectionStatusReason(const Tp::AccountPtr &account) const 0347 { 0348 if (account->connectionStatusReason() == Tp::ConnectionStatusReasonRequested) { 0349 return QString(); 0350 } else { 0351 return KTp::ErrorDictionary::displayShortErrorMessage(account->connectionError()); 0352 } 0353 } 0354 0355 QVariant KTp::AccountsListModel::get(int row, const QByteArray& role) const 0356 { 0357 //TODO: cache roles? 0358 QHash<int, QByteArray> roles = roleNames(); 0359 return index(row, 0).data(roles.key(role)); 0360 } 0361 0362 #include "accounts-list-model.moc"