1 /*
2 * issuesmodel.cpp
3 * Copyright 2019, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
4 *
5 * This file is part of Tiled.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "issuesmodel.h"
22
23 namespace Tiled {
24
IssuesModel(QObject * parent)25 IssuesModel::IssuesModel(QObject *parent)
26 : QAbstractListModel(parent)
27 {
28 mErrorIcon.addFile(QLatin1String("://images/16/dialog-error.png"));
29 mErrorIcon.addFile(QLatin1String("://images/24/dialog-error.png"));
30 mErrorIcon.addFile(QLatin1String("://images/32/dialog-error.png"));
31
32 mWarningIcon.addFile(QLatin1String("://images/16/dialog-warning.png"));
33 mWarningIcon.addFile(QLatin1String("://images/24/dialog-warning.png"));
34 mWarningIcon.addFile(QLatin1String("://images/32/dialog-warning.png"));
35
36 connect(&LoggingInterface::instance(), &LoggingInterface::issue,
37 this, &IssuesModel::addIssue);
38 connect(&LoggingInterface::instance(), &LoggingInterface::removeIssuesWithContext,
39 this, &IssuesModel::removeIssuesWithContext);
40 }
41
instance()42 IssuesModel &IssuesModel::instance()
43 {
44 static IssuesModel issuesModel;
45 return issuesModel;
46 }
47
addIssue(const Issue & issue)48 void IssuesModel::addIssue(const Issue &issue)
49 {
50 int i = mIssues.indexOf(issue);
51 if (i != -1) {
52 auto &existingIssue = mIssues[i];
53 existingIssue.addOccurrence(issue);
54
55 QModelIndex modelIndex = index(i);
56 emit dataChanged(modelIndex, modelIndex);
57 return;
58 }
59
60 switch (issue.severity()) {
61 case Issue::Error: ++mErrorCount; break;
62 case Issue::Warning: ++mWarningCount; break;
63 }
64
65 beginInsertRows(QModelIndex(), mIssues.size(), mIssues.size());
66 mIssues.append(issue);
67 endInsertRows();
68 }
69
removeIssues(const QList<unsigned> & issueIds)70 void IssuesModel::removeIssues(const QList<unsigned> &issueIds)
71 {
72 RangeSet<int> indexes;
73
74 for (unsigned id : issueIds) {
75 auto it = std::find_if(mIssues.cbegin(), mIssues.cend(),
76 [id] (const Issue &issue) { return issue.id() == id; });
77
78 if (it != mIssues.cend())
79 indexes.insert(std::distance(mIssues.cbegin(), it));
80 }
81
82 removeIssues(indexes);
83 }
84
removeIssuesWithContext(const void * context)85 void IssuesModel::removeIssuesWithContext(const void *context)
86 {
87 RangeSet<int> indexes;
88
89 for (int i = 0, size = mIssues.size(); i < size; ++i)
90 if (mIssues.at(i).context() == context)
91 indexes.insert(i);
92
93 removeIssues(indexes);
94 }
95
removeIssues(const RangeSet<int> & indexes)96 void IssuesModel::removeIssues(const RangeSet<int> &indexes)
97 {
98 if (indexes.isEmpty())
99 return;
100
101 // Remove back to front to keep the indexes valid
102 RangeSet<int>::Range it = indexes.end();
103 RangeSet<int>::Range begin = indexes.begin();
104 // assert: end != begin, since there is at least one entry
105 do {
106 --it;
107
108 std::for_each(mIssues.begin() + it.first(),
109 mIssues.begin() + it.last() + 1,
110 [this] (const Issue &issue) {
111 switch (issue.severity()) {
112 case Issue::Error: --mErrorCount; break;
113 case Issue::Warning: --mWarningCount; break;
114 }
115 });
116
117 beginRemoveRows(QModelIndex(), it.first(), it.last());
118 mIssues.remove(it.first(), it.length());
119 endRemoveRows();
120 } while (it != begin);
121 }
122
clear()123 void IssuesModel::clear()
124 {
125 beginResetModel();
126
127 mErrorCount = 0;
128 mWarningCount = 0;
129 mIssues.clear();
130
131 endResetModel();
132 }
133
rowCount(const QModelIndex & parent) const134 int IssuesModel::rowCount(const QModelIndex &parent) const
135 {
136 return parent.isValid() ? 0 : mIssues.size();
137 }
138
data(const QModelIndex & index,int role) const139 QVariant IssuesModel::data(const QModelIndex &index, int role) const
140 {
141 switch (role) {
142 case Qt::DisplayRole:
143 return mIssues.at(index.row()).text();
144 case Qt::DecorationRole:
145 switch (mIssues.at(index.row()).severity()) {
146 case Issue::Error:
147 return mErrorIcon;
148 case Issue::Warning:
149 return mWarningIcon;
150 }
151 break;
152 case Qt::BackgroundRole: {
153 switch (mIssues.at(index.row()).severity()) {
154 case Issue::Error:
155 return QColor(253, 0, 69, 32);
156 case Issue::Warning:
157 return QColor(255, 230, 0, 32);
158 }
159 break;
160 }
161 case IssueRole:
162 return QVariant::fromValue(mIssues.at(index.row()));
163 }
164
165 return QVariant();
166 }
167
168 } // namespace Tiled
169
170 #include "moc_issuesmodel.cpp"
171