1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#include "kaboutapplicationpersonmodel_p.h"
9#include "debug.h"
10
11#include <QNetworkAccessManager>
12#include <QNetworkRequest>
13
14namespace KDEPrivate
15{
16KAboutApplicationPersonModel::KAboutApplicationPersonModel(const QList<KAboutPerson> &personList, QObject *parent)
17 : QAbstractListModel(parent)
18 , m_personList(personList)
19{
20 m_profileList.reserve(asize: m_personList.size());
21 bool hasAnyAvatars{false};
22 for (const auto &person : std::as_const(t: m_personList)) {
23 KAboutApplicationPersonProfile profile = KAboutApplicationPersonProfile(person.name(), person.task(), person.emailAddress(), person.avatarUrl());
24 profile.setHomepage(QUrl(person.webAddress()));
25 if (!profile.avatarUrl().isEmpty()) {
26 hasAnyAvatars = true;
27 }
28 m_profileList.append(t: profile);
29 }
30 m_hasAnyAvatars = hasAnyAvatars;
31
32 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
33 manager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
34 connect(sender: this, signal: &KAboutApplicationPersonModel::showRemoteAvatarsChanged, slot: [this, manager]() {
35 if (showRemoteAvatars()) {
36 int i = 0;
37 for (const auto &profile : std::as_const(t&: m_profileList)) {
38 if (!profile.avatarUrl().isEmpty()) {
39 if (profile.avatar().isNull()) {
40 QNetworkRequest request(profile.avatarUrl());
41 request.setAttribute(code: QNetworkRequest::CacheLoadControlAttribute, value: QNetworkRequest::PreferCache);
42 QNetworkReply *reply = manager->get(request);
43 reply->setProperty(name: "personProfile", value: i);
44 connect(sender: reply, signal: &QNetworkReply::finished, context: this, slot: &KAboutApplicationPersonModel::onAvatarJobFinished);
45 m_ongoingAvatarFetches << reply;
46 } else {
47 Q_EMIT dataChanged(topLeft: index(row: i), bottomRight: index(row: i));
48 }
49 }
50 ++i;
51 }
52 } else {
53 // We keep the avatars around, no use deleting them - just hide them in the UI
54 // This way we don't cause unnecessary crunching on the user's connection if
55 // they do a bunch of toggling. Just stop the current fetches, and trust the
56 // consumer to understand it should not show avatars if this property is false.
57 for (QNetworkReply *reply : m_ongoingAvatarFetches) {
58 reply->abort();
59 }
60 m_ongoingAvatarFetches.clear();
61 Q_EMIT dataChanged(topLeft: index(row: 0), bottomRight: index(row: m_profileList.count() - 1));
62 }
63 });
64}
65
66int KAboutApplicationPersonModel::rowCount(const QModelIndex &parent) const
67{
68 Q_UNUSED(parent)
69 return m_personList.count();
70}
71
72QVariant KAboutApplicationPersonModel::data(const QModelIndex &index, int role) const
73{
74 if (!index.isValid()) {
75 qCWarning(DEBUG_KXMLGUI) << "ERROR: invalid index";
76 return QVariant();
77 }
78 if (index.row() >= rowCount()) {
79 qCWarning(DEBUG_KXMLGUI) << "ERROR: index out of bounds";
80 return QVariant();
81 }
82 if (role == Qt::DisplayRole) {
83 // qCDebug(DEBUG_KXMLGUI) << "Spitting data for name " << m_profileList.at( index.row() ).name();
84 QVariant var;
85 var.setValue(m_profileList.at(i: index.row()));
86 return var;
87 } else {
88 return QVariant();
89 }
90}
91
92Qt::ItemFlags KAboutApplicationPersonModel::flags(const QModelIndex &index) const
93{
94 if (index.isValid()) {
95 return Qt::ItemIsEnabled;
96 }
97 return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
98}
99
100void KAboutApplicationPersonModel::onAvatarJobFinished() // SLOT
101{
102 QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: sender());
103 if (reply) {
104 m_ongoingAvatarFetches.removeAll(t: reply);
105 if (reply->error() == QNetworkReply::NoError) {
106 int personProfileListIndex = reply->property(name: "personProfile").toInt();
107 QNetworkAccessManager *manager = reply->manager();
108 if (manager) {
109 if (reply->error() != QNetworkReply::NoError) {
110 Q_EMIT dataChanged(topLeft: index(row: personProfileListIndex), bottomRight: index(row: personProfileListIndex));
111 return;
112 }
113 reply->waitForReadyRead(msecs: 1000);
114 QByteArray data = reply->readAll();
115 QPixmap pixmap;
116 pixmap.loadFromData(buf: data);
117
118 KAboutApplicationPersonProfile profile = m_profileList.value(i: personProfileListIndex);
119 if (!pixmap.isNull()) {
120 profile.setAvatar(pixmap);
121 if (!m_hasAvatarPixmaps) {
122 m_hasAvatarPixmaps = true;
123 // Data has changed for all the elements now... otherwise layouts will be all wonky in our delegates
124 Q_EMIT dataChanged(topLeft: index(row: 0), bottomRight: index(row: m_profileList.count() - 1));
125 }
126 } else {
127 // Failed to read pixmap data, so... let's load something useful maybe?
128 }
129
130 m_profileList.replace(i: personProfileListIndex, t: profile);
131 Q_EMIT dataChanged(topLeft: index(row: personProfileListIndex), bottomRight: index(row: personProfileListIndex));
132 }
133 }
134 reply->deleteLater();
135 }
136}
137
138} // namespace KDEPrivate
139
140#include "moc_kaboutapplicationpersonmodel_p.cpp"
141

source code of kxmlgui/src/kaboutapplicationpersonmodel_p.cpp