File indexing completed on 2024-11-10 06:43:16

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "kaboutapplicationpersonmodel_p.h"
0009 #include "debug.h"
0010 
0011 #include <QNetworkAccessManager>
0012 #include <QNetworkRequest>
0013 
0014 namespace KDEPrivate
0015 {
0016 KAboutApplicationPersonModel::KAboutApplicationPersonModel(const QList<KAboutPerson> &personList, QObject *parent)
0017     : QAbstractListModel(parent)
0018     , m_personList(personList)
0019 {
0020     m_profileList.reserve(m_personList.size());
0021     bool hasAnyAvatars{false};
0022     for (const auto &person : std::as_const(m_personList)) {
0023         KAboutApplicationPersonProfile profile = KAboutApplicationPersonProfile(person.name(), person.task(), person.emailAddress(), person.avatarUrl());
0024         profile.setHomepage(QUrl(person.webAddress()));
0025         if (!profile.avatarUrl().isEmpty()) {
0026             hasAnyAvatars = true;
0027         }
0028         m_profileList.append(profile);
0029     }
0030     m_hasAnyAvatars = hasAnyAvatars;
0031 
0032     QNetworkAccessManager *manager = new QNetworkAccessManager(this);
0033     manager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
0034     connect(this, &KAboutApplicationPersonModel::showRemoteAvatarsChanged, [this, manager]() {
0035         if (showRemoteAvatars()) {
0036             int i = 0;
0037             for (const auto &profile : std::as_const(m_profileList)) {
0038                 if (!profile.avatarUrl().isEmpty()) {
0039                     if (profile.avatar().isNull()) {
0040                         QNetworkRequest request(profile.avatarUrl());
0041                         request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
0042                         QNetworkReply *reply = manager->get(request);
0043                         reply->setProperty("personProfile", i);
0044                         connect(reply, &QNetworkReply::finished, this, &KAboutApplicationPersonModel::onAvatarJobFinished);
0045                         m_ongoingAvatarFetches << reply;
0046                     } else {
0047                         Q_EMIT dataChanged(index(i), index(i));
0048                     }
0049                 }
0050                 ++i;
0051             }
0052         } else {
0053             // We keep the avatars around, no use deleting them - just hide them in the UI
0054             // This way we don't cause unnecessary crunching on the user's connection if
0055             // they do a bunch of toggling. Just stop the current fetches, and trust the
0056             // consumer to understand it should not show avatars if this property is false.
0057             for (QNetworkReply *reply : m_ongoingAvatarFetches) {
0058                 reply->abort();
0059             }
0060             m_ongoingAvatarFetches.clear();
0061             Q_EMIT dataChanged(index(0), index(m_profileList.count() - 1));
0062         }
0063     });
0064 }
0065 
0066 int KAboutApplicationPersonModel::rowCount(const QModelIndex &parent) const
0067 {
0068     Q_UNUSED(parent)
0069     return m_personList.count();
0070 }
0071 
0072 QVariant KAboutApplicationPersonModel::data(const QModelIndex &index, int role) const
0073 {
0074     if (!index.isValid()) {
0075         qCWarning(DEBUG_KXMLGUI) << "ERROR: invalid index";
0076         return QVariant();
0077     }
0078     if (index.row() >= rowCount()) {
0079         qCWarning(DEBUG_KXMLGUI) << "ERROR: index out of bounds";
0080         return QVariant();
0081     }
0082     if (role == Qt::DisplayRole) {
0083         //        qCDebug(DEBUG_KXMLGUI) << "Spitting data for name " << m_profileList.at( index.row() ).name();
0084         QVariant var;
0085         var.setValue(m_profileList.at(index.row()));
0086         return var;
0087     } else {
0088         return QVariant();
0089     }
0090 }
0091 
0092 Qt::ItemFlags KAboutApplicationPersonModel::flags(const QModelIndex &index) const
0093 {
0094     if (index.isValid()) {
0095         return Qt::ItemIsEnabled;
0096     }
0097     return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
0098 }
0099 
0100 void KAboutApplicationPersonModel::onAvatarJobFinished() // SLOT
0101 {
0102     QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
0103     if (reply) {
0104         m_ongoingAvatarFetches.removeAll(reply);
0105         if (reply->error() == QNetworkReply::NoError) {
0106             int personProfileListIndex = reply->property("personProfile").toInt();
0107             QNetworkAccessManager *manager = reply->manager();
0108             if (manager) {
0109                 if (reply->error() != QNetworkReply::NoError) {
0110                     Q_EMIT dataChanged(index(personProfileListIndex), index(personProfileListIndex));
0111                     return;
0112                 }
0113                 reply->waitForReadyRead(1000);
0114                 QByteArray data = reply->readAll();
0115                 QPixmap pixmap;
0116                 pixmap.loadFromData(data);
0117 
0118                 KAboutApplicationPersonProfile profile = m_profileList.value(personProfileListIndex);
0119                 if (!pixmap.isNull()) {
0120                     profile.setAvatar(pixmap);
0121                     if (!m_hasAvatarPixmaps) {
0122                         m_hasAvatarPixmaps = true;
0123                         // Data has changed for all the elements now... otherwise layouts will be all wonky in our delegates
0124                         Q_EMIT dataChanged(index(0), index(m_profileList.count() - 1));
0125                     }
0126                 } else {
0127                     // Failed to read pixmap data, so... let's load something useful maybe?
0128                 }
0129 
0130                 m_profileList.replace(personProfileListIndex, profile);
0131                 Q_EMIT dataChanged(index(personProfileListIndex), index(personProfileListIndex));
0132             }
0133         }
0134         reply->deleteLater();
0135     }
0136 }
0137 
0138 } // namespace KDEPrivate
0139 
0140 #include "moc_kaboutapplicationpersonmodel_p.cpp"