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