1 /*
2 kjobmodel.cpp
3
4 This file is part of GammaRay, the Qt application inspection and
5 manipulation tool.
6
7 Copyright (C) 2012-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8 Author: Volker Krause <volker.krause@kdab.com>
9
10 Licensees holding valid commercial KDAB GammaRay licenses may use this file in
11 accordance with GammaRay Commercial License Agreement provided with the Software.
12
13 Contact info@kdab.com if any conditions of this licensing are not clear to you.
14
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation, either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 */
28
29 #include "kjobmodel.h"
30 #include <core/util.h>
31
32 #include <KJob>
33
34 #include <QGuiApplication>
35 #include <QPalette>
36
37 using namespace GammaRay;
38
39 /*
40 * TODO
41 * - show job hierarchy
42 * - show all job info messages
43 * - show progress information
44 * - allow to cancel/suspend if job supports that
45 * - track runtime
46 * - allow to clear the model
47 * - tooltips with additional information (capabilities etc)
48 */
49
KJobModel(QObject * parent)50 KJobModel::KJobModel(QObject *parent)
51 : QAbstractTableModel(parent)
52 {
53 }
54
data(const QModelIndex & index,int role) const55 QVariant KJobModel::data(const QModelIndex &index, int role) const
56 {
57 if (!index.isValid())
58 return QVariant();
59
60 const KJobInfo &job = m_data.at(index.row());
61 if (role == Qt::DisplayRole) {
62 switch (index.column()) {
63 case 0:
64 return job.name;
65 case 1:
66 return job.type;
67 case 2:
68 return job.statusText;
69 }
70 } else if (role == Qt::ForegroundRole) {
71 switch (job.state) {
72 case KJobInfo::Finished:
73 case KJobInfo::Deleted:
74 return qApp->palette().brush(QPalette::Disabled, QPalette::Foreground);
75 case KJobInfo::Error:
76 return QVariant::fromValue<QColor>(Qt::red);
77 case KJobInfo::Killed:
78 return qApp->palette().link();
79 default:
80 return QVariant();
81 }
82 }
83
84 return QVariant();
85 }
86
columnCount(const QModelIndex & parent) const87 int KJobModel::columnCount(const QModelIndex &parent) const
88 {
89 Q_UNUSED(parent);
90 return 3;
91 }
92
rowCount(const QModelIndex & parent) const93 int KJobModel::rowCount(const QModelIndex &parent) const
94 {
95 if (parent.isValid())
96 return 0;
97 return m_data.size();
98 }
99
headerData(int section,Qt::Orientation orientation,int role) const100 QVariant KJobModel::headerData(int section, Qt::Orientation orientation, int role) const
101 {
102 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
103 switch (section) {
104 case 0:
105 return tr("Job");
106 case 1:
107 return tr("Type");
108 case 2:
109 return tr("Status");
110 }
111 }
112 return QAbstractItemModel::headerData(section, orientation, role);
113 }
114
objectAdded(QObject * obj)115 void KJobModel::objectAdded(QObject *obj)
116 {
117 KJob *job = qobject_cast<KJob *>(obj);
118 if (!job)
119 return;
120
121 beginInsertRows(QModelIndex(), rowCount(), rowCount());
122 KJobInfo jobInfo;
123 jobInfo.job = job;
124 connect(job, &KJob::result, this, &KJobModel::jobResult);
125 connect(job, &KJob::finished, this, &KJobModel::jobFinished);
126 connect(job, &KJob::infoMessage, this, &KJobModel::jobInfo);
127 jobInfo.name = obj->objectName().isEmpty() ? Util::addressToString(obj) : obj->objectName();
128 jobInfo.type = obj->metaObject()->className();
129 jobInfo.state = KJobInfo::Running;
130 m_data.push_back(jobInfo);
131 endInsertRows();
132 }
133
objectRemoved(QObject * obj)134 void KJobModel::objectRemoved(QObject *obj)
135 {
136 const int pos = indexOfJob(obj);
137 if (pos < 0)
138 return;
139
140 // KJob dtor emits finished, so this shouldn't happen, in theory
141 // We however seem to get here for very short-lived jobs that emit before objectAdded()
142 // is called (while we wait for the vtable to be complete), so we only see the result
143 // of their deleteLater().
144 if (m_data.at(pos).state == KJobInfo::Running) {
145 m_data[pos].state = KJobInfo::Deleted;
146 m_data[pos].statusText = tr("Deleted");
147 emit dataChanged(index(pos, 0), index(pos, columnCount() - 1));
148 }
149 }
150
jobResult(KJob * job)151 void KJobModel::jobResult(KJob *job)
152 {
153 const int pos = indexOfJob(job);
154 if (pos < 0)
155 return;
156
157 if (job->error()) {
158 m_data[pos].state = KJobInfo::Error;
159 m_data[pos].statusText = job->errorString();
160 } else {
161 if (m_data.at(pos).state == KJobInfo::Killed) {
162 // we can get finished() before result(), which is perfectly fine
163 m_data[pos].statusText.clear();
164 }
165 m_data[pos].state = KJobInfo::Finished;
166 }
167
168 emit dataChanged(index(pos, 0), index(pos, columnCount()-1));
169 }
170
jobFinished(KJob * obj)171 void KJobModel::jobFinished(KJob *obj)
172 {
173 const int pos = indexOfJob(obj);
174 if (pos < 0)
175 return;
176
177 if (m_data.at(pos).state == KJobInfo::Running) {
178 m_data[pos].state = KJobInfo::Killed;
179 m_data[pos].statusText = tr("Killed");
180 }
181
182 emit dataChanged(index(pos, 0), index(pos, columnCount()-1));
183 }
184
jobInfo(KJob * job,const QString & plainMessage)185 void KJobModel::jobInfo(KJob *job, const QString &plainMessage)
186 {
187 const int pos = indexOfJob(job);
188 if (pos < 0)
189 return;
190
191 if (m_data.at(pos).state == KJobInfo::Running)
192 m_data[pos].statusText = plainMessage;
193
194 emit dataChanged(index(pos, 0), index(pos, columnCount()-1));
195 }
196
indexOfJob(QObject * obj) const197 int KJobModel::indexOfJob(QObject *obj) const
198 {
199 for (int i = 0; i < m_data.size(); ++i) {
200 if (m_data.at(i).job == obj)
201 return i;
202 }
203 return -1;
204 }
205