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"