1 /*
2 Drawpile - a collaborative drawing program.
3
4 Copyright (C) 2007-2019 Calle Laakkonen
5
6 Drawpile is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Drawpile is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Drawpile. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "userlist.h"
21 #include "utils/icon.h"
22 #include "../libshared/net/meta.h"
23 #include "../libshared/net/meta2.h"
24
25 #include <QDebug>
26 #include <QJsonArray>
27 #include <QPixmap>
28 #include <QPalette>
29
30 namespace canvas {
31
UserListModel(QObject * parent)32 UserListModel::UserListModel(QObject *parent)
33 : QAbstractTableModel(parent)
34 {
35 m_onlineUsers = new OnlineUserListModel(this);
36 m_onlineUsers->setSourceModel(this);
37 }
38
39
data(const QModelIndex & index,int role) const40 QVariant UserListModel::data(const QModelIndex& index, int role) const
41 {
42 if(!index.isValid() || index.row() < 0 || index.row() >= m_users.size())
43 return QVariant();
44
45 const User &u = m_users.at(index.row());
46
47 if(role == Qt::ForegroundRole && !u.isOnline) {
48 return QPalette().color(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Foreground);
49 }
50
51 if(index.column() == 0) {
52 switch(role) {
53 case IdRole: return u.id;
54 case Qt::DisplayRole:
55 case NameRole: return u.name;
56 case Qt::DecorationRole:
57 case AvatarRole: return u.avatar;
58 case IsOpRole: return u.isOperator;
59 case IsTrustedRole: return u.isTrusted;
60 case IsModRole: return u.isMod;
61 case IsAuthRole: return u.isAuth;
62 case IsBotRole: return u.isBot;
63 case IsLockedRole: return u.isLocked;
64 case IsMutedRole: return u.isMuted;
65 case IsOnlineRole: return u.isOnline;
66 }
67
68 } else if(role==Qt::DisplayRole) {
69 switch(index.column()) {
70 case 1:
71 if(u.isMod)
72 return tr("Moderator");
73 else if(u.isOperator)
74 return tr("Operator");
75 else if(u.isTrusted)
76 return tr("Trusted");
77 else if(u.isAuth)
78 return tr("Registered");
79 else
80 return QVariant();
81
82 case 2: return u.isOnline ? tr("Online") : tr("Offline");
83 }
84
85 } else if(role==Qt::DecorationRole) {
86 switch(index.column()) {
87 case 3: return u.isLocked ? icon::fromTheme("object-locked") : QVariant();
88 case 4: return u.isMuted ? icon::fromTheme("irc-unvoice") : QVariant();
89 }
90 }
91
92 return QVariant();
93 }
94
columnCount(const QModelIndex &) const95 int UserListModel::columnCount(const QModelIndex&) const
96 {
97 return 5;
98 }
99
rowCount(const QModelIndex & parent) const100 int UserListModel::rowCount(const QModelIndex& parent) const
101 {
102 if(parent.isValid())
103 return 0;
104 return m_users.count();
105 }
106
headerData(int section,Qt::Orientation orientation,int role) const107 QVariant UserListModel::headerData(int section, Qt::Orientation orientation, int role) const
108 {
109 if(orientation == Qt::Horizontal) {
110 if(role == Qt::DisplayRole) {
111 switch(section) {
112 case 0: return tr("User");
113 case 1: return tr("Type");
114 case 2: return tr("Status");
115 }
116 } else if(role == Qt::DecorationRole) {
117 switch(section) {
118 case 3: return icon::fromTheme("object-locked");
119 case 4: return icon::fromTheme("irc-unvoice");
120 }
121 }
122
123 } else if(section >=0 && section < m_users.size() && role == Qt::DisplayRole) {
124 return m_users.at(section).id;
125 }
126
127 return QVariant();
128 }
129
userLogin(const User & user)130 void UserListModel::userLogin(const User &user)
131 {
132 // Check if this is a returning user
133 for(int i=0;i<m_users.count();++i) {
134 User &u = m_users[i];
135 if(u.id == user.id) {
136 u.name = user.name;
137 u.avatar = user.avatar;
138 u.isLocal = user.isLocal;
139 u.isAuth = user.isAuth;
140 u.isMod = user.isMod;
141 u.isBot = user.isBot;
142 u.isMuted = user.isMuted;
143 u.isOnline = true;
144
145 emit dataChanged(index(i, 0), index(i, columnCount()-1));
146 return;
147 }
148 }
149
150 // Add new user
151 int pos = m_users.count();
152 beginInsertRows(QModelIndex(),pos,pos);
153 m_users.append(user);
154 endInsertRows();
155 }
156
userLogout(int id)157 void UserListModel::userLogout(int id)
158 {
159 for(int i=0;i<m_users.size();++i) {
160 if(m_users.at(i).id == id) {
161 m_users[i].isOnline = false;
162 emit dataChanged(index(i, 0), index(i, columnCount()-1));
163 return;
164 }
165 }
166 }
167
allLogout()168 void UserListModel::allLogout()
169 {
170 if(!m_users.isEmpty()) {
171 for(int i=0;i<m_users.size();++i)
172 m_users[i].isOnline = false;
173 emit dataChanged(
174 index(0, 0),
175 index(m_users.size()-1, columnCount()-1)
176 );
177 }
178 }
179
reset()180 void UserListModel::reset()
181 {
182 beginRemoveRows(QModelIndex(), 0, m_users.size()-1);
183 m_users.clear();
184 endRemoveRows();
185 }
186
updateOperators(const QList<uint8_t> ids)187 void UserListModel::updateOperators(const QList<uint8_t> ids)
188 {
189 for(int i=0;i<m_users.size();++i) {
190 User &u = m_users[i];
191
192 const bool op = ids.contains(u.id);
193 if(op != u.isOperator) {
194 u.isOperator = op;
195 emit dataChanged(index(i, 0), index(i, columnCount()-1));
196 }
197 }
198 }
199
updateTrustedUsers(const QList<uint8_t> trustedIds)200 void UserListModel::updateTrustedUsers(const QList<uint8_t> trustedIds)
201 {
202 for(int i=0;i<m_users.size();++i) {
203 User &u = m_users[i];
204
205 const bool trusted = trustedIds.contains(u.id);
206 if(trusted != u.isTrusted) {
207 u.isTrusted = trusted;
208 emit dataChanged(index(i, 0), index(i, columnCount()-1));
209 }
210 }
211 }
212
updateLocks(const QList<uint8_t> ids)213 void UserListModel::updateLocks(const QList<uint8_t> ids)
214 {
215 for(int i=0;i<m_users.size();++i) {
216 User &u = m_users[i];
217
218 const bool lock = ids.contains(u.id);
219 if(lock != u.isLocked) {
220 u.isLocked = lock;
221 emit dataChanged(index(i, 0), index(i, columnCount()-1));
222 }
223 }
224 }
225
updateMuteList(const QJsonArray & mutedUserIds)226 void UserListModel::updateMuteList(const QJsonArray &mutedUserIds)
227 {
228 for(int i=0;i<m_users.size();++i) {
229 User &u = m_users[i];
230 const bool mute = mutedUserIds.contains(u.id);
231 if(u.isMuted != mute) {
232 u.isMuted = mute;
233 emit dataChanged(index(i, 0), index(i, columnCount()-1));
234 }
235 }
236 }
237
operatorList() const238 QList<uint8_t> UserListModel::operatorList() const
239 {
240 QList<uint8_t> ops;
241 for(int i=0;i<m_users.size();++i) {
242 const User &u = m_users.at(i);
243 if(u.isOnline && (u.isOperator || u.isMod))
244 ops << u.id;
245 }
246 return ops;
247 }
248
lockList() const249 QList<uint8_t> UserListModel::lockList() const
250 {
251 QList<uint8_t> locks;
252 for(int i=0;i<m_users.size();++i) {
253 const User &u = m_users.at(i);
254 if(u.isOnline && u.isLocked)
255 locks << m_users.at(i).id;
256 }
257 return locks;
258 }
259
trustedList() const260 QList<uint8_t> UserListModel::trustedList() const
261 {
262 QList<uint8_t> ids;
263 for(int i=0;i<m_users.size();++i) {
264 const User &u = m_users.at(i);
265 if(u.isOnline && u.isTrusted)
266 ids << m_users.at(i).id;
267 }
268 return ids;
269 }
270
getUserById(int id) const271 User UserListModel::getUserById(int id) const
272 {
273 for(const User &u : m_users)
274 if(u.id == id)
275 return u;
276
277 // Nothing found
278 return User();
279 }
280
getUsername(int id) const281 QString UserListModel::getUsername(int id) const
282 {
283 // Special case: id 0 is reserved for the server
284 if(id==0)
285 return tr("Server");
286
287 for(const User &u : m_users)
288 if(u.id == id)
289 return u.name;
290
291 // Not found
292 return tr("User #%1").arg(id);
293 }
294
getLockUserCommand(int localId,int userId,bool lock) const295 protocol::MessagePtr UserListModel::getLockUserCommand(int localId, int userId, bool lock) const
296 {
297 Q_ASSERT(userId>0 && userId<255);
298
299 QList<uint8_t> ids = lockList();
300 if(lock) {
301 if(!ids.contains(userId))
302 ids.append(userId);
303 } else {
304 ids.removeAll(userId);
305 }
306
307 return protocol::MessagePtr(new protocol::UserACL(localId, ids));
308 }
309
getOpUserCommand(int localId,int userId,bool op) const310 protocol::MessagePtr UserListModel::getOpUserCommand(int localId, int userId, bool op) const
311 {
312 Q_ASSERT(userId>0 && userId<255);
313
314 QList<uint8_t> ops = operatorList();
315 if(op) {
316 if(!ops.contains(userId))
317 ops.append(userId);
318 } else {
319 ops.removeOne(userId);
320 }
321
322 return protocol::MessagePtr(new protocol::SessionOwner(localId, ops));
323 }
324
getTrustUserCommand(int localId,int userId,bool trust) const325 protocol::MessagePtr UserListModel::getTrustUserCommand(int localId, int userId, bool trust) const
326 {
327 Q_ASSERT(userId>0 && userId<255);
328
329 QList<uint8_t> trusted = trustedList();
330 if(trust) {
331 if(!trusted.contains(userId))
332 trusted.append(userId);
333 } else {
334 trusted.removeOne(userId);
335 }
336
337 return protocol::MessagePtr(new protocol::TrustedUsers(localId, trusted));
338 }
339
filterAcceptsRow(int source_row,const QModelIndex & parent) const340 bool OnlineUserListModel::filterAcceptsRow(int source_row, const QModelIndex &parent) const
341 {
342 const auto i = sourceModel()->index(source_row, 0);
343 return i.data(UserListModel::IsOnlineRole).toBool() && QSortFilterProxyModel::filterAcceptsRow(source_row, parent);
344 }
345
346 }
347