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 | |
14 | namespace KDEPrivate |
15 | { |
16 | KAboutApplicationPersonModel::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 | |
66 | int KAboutApplicationPersonModel::rowCount(const QModelIndex &parent) const |
67 | { |
68 | Q_UNUSED(parent) |
69 | return m_personList.count(); |
70 | } |
71 | |
72 | QVariant 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 | |
92 | Qt::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 | |
100 | void 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 | |