|
1 #include <QDebug> |
|
2 #include <QFile> |
|
3 #include <QModelIndex> |
|
4 #include <QModelIndexList> |
|
5 #include <QPainter> |
|
6 #include <QTextStream> |
|
7 |
|
8 #include "players_model.h" |
|
9 |
|
10 PlayersListModel::PlayersListModel(QObject *parent) |
|
11 : QAbstractListModel(parent) { |
|
12 m_fontInRoom = QFont(); |
|
13 m_fontInRoom.setItalic(true); |
|
14 } |
|
15 |
|
16 int PlayersListModel::rowCount(const QModelIndex &parent) const { |
|
17 if (parent.isValid()) |
|
18 return 0; |
|
19 else |
|
20 return m_data.size(); |
|
21 } |
|
22 |
|
23 QVariant PlayersListModel::data(const QModelIndex &index, int role) const { |
|
24 if (!index.isValid() || index.row() < 0 || index.row() >= rowCount() || |
|
25 index.column() != 0) |
|
26 return QVariant(QVariant::Invalid); |
|
27 |
|
28 return m_data.at(index.row()).value(role); |
|
29 } |
|
30 |
|
31 bool PlayersListModel::setData(const QModelIndex &index, const QVariant &value, |
|
32 int role) { |
|
33 if (!index.isValid() || index.row() < 0 || index.row() >= rowCount() || |
|
34 index.column() != 0) |
|
35 return false; |
|
36 |
|
37 m_data[index.row()].insert(role, value); |
|
38 |
|
39 emit dataChanged(index, index); |
|
40 |
|
41 return true; |
|
42 } |
|
43 |
|
44 bool PlayersListModel::insertRows(int row, int count, |
|
45 const QModelIndex &parent) { |
|
46 if (parent.isValid() || row > rowCount() || row < 0 || count < 1) |
|
47 return false; |
|
48 |
|
49 beginInsertRows(parent, row, row + count - 1); |
|
50 |
|
51 for (int i = 0; i < count; ++i) m_data.insert(row, DataEntry()); |
|
52 |
|
53 endInsertRows(); |
|
54 |
|
55 return true; |
|
56 } |
|
57 |
|
58 bool PlayersListModel::removeRows(int row, int count, |
|
59 const QModelIndex &parent) { |
|
60 if (parent.isValid() || row + count > rowCount() || row < 0 || count < 1) |
|
61 return false; |
|
62 |
|
63 beginRemoveRows(parent, row, row + count - 1); |
|
64 |
|
65 for (int i = 0; i < count; ++i) m_data.removeAt(row); |
|
66 |
|
67 endRemoveRows(); |
|
68 |
|
69 return true; |
|
70 } |
|
71 |
|
72 QModelIndex PlayersListModel::nicknameIndex(const QString &nickname) { |
|
73 QModelIndexList mil = |
|
74 match(index(0), Qt::DisplayRole, nickname, 1, Qt::MatchExactly); |
|
75 |
|
76 if (mil.size() > 0) |
|
77 return mil[0]; |
|
78 else |
|
79 return QModelIndex(); |
|
80 } |
|
81 |
|
82 void PlayersListModel::addPlayer(const QString &nickname, bool notify) { |
|
83 insertRow(rowCount()); |
|
84 |
|
85 QModelIndex mi = index(rowCount() - 1); |
|
86 setData(mi, nickname); |
|
87 |
|
88 checkFriendIgnore(mi); |
|
89 |
|
90 emit nickAddedLobby(nickname, notify); |
|
91 } |
|
92 |
|
93 void PlayersListModel::removePlayer(const QString &nickname, |
|
94 const QString &msg) { |
|
95 if (msg.isEmpty()) |
|
96 emit nickRemovedLobby(nickname, QString()); |
|
97 else |
|
98 emit nickRemovedLobby(nickname, msg); |
|
99 |
|
100 QModelIndex mi = nicknameIndex(nickname); |
|
101 |
|
102 if (mi.isValid()) removeRow(mi.row()); |
|
103 } |
|
104 |
|
105 void PlayersListModel::playerJoinedRoom(const QString &nickname, bool notify) { |
|
106 QModelIndex mi = nicknameIndex(nickname); |
|
107 |
|
108 if (mi.isValid()) { |
|
109 setData(mi, true, RoomFilterRole); |
|
110 updateIcon(mi); |
|
111 updateSortData(mi); |
|
112 } |
|
113 |
|
114 emit nickAdded(nickname, notify); |
|
115 } |
|
116 |
|
117 void PlayersListModel::playerLeftRoom(const QString &nickname) { |
|
118 emit nickRemoved(nickname); |
|
119 |
|
120 QModelIndex mi = nicknameIndex(nickname); |
|
121 |
|
122 if (mi.isValid()) { |
|
123 setData(mi, false, RoomFilterRole); |
|
124 setData(mi, false, RoomAdmin); |
|
125 setData(mi, false, Ready); |
|
126 setData(mi, false, InGame); |
|
127 updateIcon(mi); |
|
128 } |
|
129 } |
|
130 |
|
131 void PlayersListModel::setFlag(const QString &nickname, StateFlag flagType, |
|
132 bool isSet) { |
|
133 if (flagType == Friend) { |
|
134 if (isSet) |
|
135 m_friendsSet.insert(nickname.toLower()); |
|
136 else |
|
137 m_friendsSet.remove(nickname.toLower()); |
|
138 |
|
139 // FIXME: set proper file name |
|
140 // saveSet(m_friendsSet, "friends"); |
|
141 } else if (flagType == Ignore) { |
|
142 if (isSet) |
|
143 m_ignoredSet.insert(nickname.toLower()); |
|
144 else |
|
145 m_ignoredSet.remove(nickname.toLower()); |
|
146 |
|
147 // FIXME: set proper file name |
|
148 // saveSet(m_ignoredSet, "ignore"); |
|
149 } |
|
150 |
|
151 QModelIndex mi = nicknameIndex(nickname); |
|
152 |
|
153 if (mi.isValid()) { |
|
154 setData(mi, isSet, flagType); |
|
155 |
|
156 if (flagType == Friend || flagType == ServerAdmin || flagType == Ignore || |
|
157 flagType == RoomAdmin) |
|
158 updateSortData(mi); |
|
159 |
|
160 updateIcon(mi); |
|
161 } |
|
162 } |
|
163 |
|
164 bool PlayersListModel::isFlagSet(const QString &nickname, StateFlag flagType) { |
|
165 QModelIndex mi = nicknameIndex(nickname); |
|
166 |
|
167 if (mi.isValid()) |
|
168 return mi.data(flagType).toBool(); |
|
169 else if (flagType == Friend) |
|
170 return isFriend(nickname); |
|
171 else if (flagType == Ignore) |
|
172 return isIgnored(nickname); |
|
173 else |
|
174 return false; |
|
175 } |
|
176 |
|
177 void PlayersListModel::resetRoomFlags() { |
|
178 for (int i = rowCount() - 1; i >= 0; --i) { |
|
179 QModelIndex mi = index(i); |
|
180 |
|
181 if (mi.data(RoomFilterRole).toBool()) { |
|
182 setData(mi, false, RoomFilterRole); |
|
183 setData(mi, false, RoomAdmin); |
|
184 setData(mi, false, Ready); |
|
185 setData(mi, false, InGame); |
|
186 |
|
187 updateSortData(mi); |
|
188 updateIcon(mi); |
|
189 } |
|
190 } |
|
191 } |
|
192 |
|
193 void PlayersListModel::updateIcon(const QModelIndex &index) { |
|
194 quint32 iconNum = 0; |
|
195 |
|
196 QList<bool> flags; |
|
197 flags << index.data(Ready).toBool() << index.data(ServerAdmin).toBool() |
|
198 << index.data(RoomAdmin).toBool() << index.data(Registered).toBool() |
|
199 << index.data(Friend).toBool() << index.data(Ignore).toBool() |
|
200 << index.data(InGame).toBool() << index.data(RoomFilterRole).toBool() |
|
201 << index.data(InRoom).toBool() << index.data(Contributor).toBool(); |
|
202 |
|
203 for (int i = flags.size() - 1; i >= 0; --i) |
|
204 if (flags[i]) iconNum |= 1 << i; |
|
205 |
|
206 if (m_icons().contains(iconNum)) { |
|
207 setData(index, m_icons().value(iconNum), Qt::DecorationRole); |
|
208 } else { |
|
209 QPixmap result(24, 16); |
|
210 result.fill(Qt::transparent); |
|
211 |
|
212 QPainter painter(&result); |
|
213 |
|
214 if (index.data(RoomFilterRole).toBool()) { |
|
215 if (index.data(InGame).toBool()) { |
|
216 painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/chat/ingame.png")); |
|
217 } else { |
|
218 if (index.data(Ready).toBool()) |
|
219 painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/chat/lamp.png")); |
|
220 else |
|
221 painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/chat/lamp_off.png")); |
|
222 } |
|
223 } else { // we're in lobby |
|
224 if (!index.data(InRoom).toBool()) |
|
225 painter.drawPixmap(0, 0, 16, 16, QPixmap(":/res/Flake.png")); |
|
226 } |
|
227 |
|
228 QString mainIconName(":/res/chat/"); |
|
229 |
|
230 if (index.data(ServerAdmin).toBool()) |
|
231 mainIconName += "serveradmin"; |
|
232 else { |
|
233 if (index.data(RoomAdmin).toBool()) |
|
234 mainIconName += "roomadmin"; |
|
235 else |
|
236 mainIconName += "hedgehog"; |
|
237 |
|
238 if (index.data(Contributor).toBool()) mainIconName += "contributor"; |
|
239 } |
|
240 |
|
241 if (!index.data(Registered).toBool()) mainIconName += "_gray"; |
|
242 |
|
243 painter.drawPixmap(8, 0, 16, 16, QPixmap(mainIconName + ".png")); |
|
244 |
|
245 if (index.data(Ignore).toBool()) |
|
246 painter.drawPixmap(8, 0, 16, 16, QPixmap(":/res/chat/ignore.png")); |
|
247 else if (index.data(Friend).toBool()) |
|
248 painter.drawPixmap(8, 0, 16, 16, QPixmap(":/res/chat/friend.png")); |
|
249 |
|
250 painter.end(); |
|
251 |
|
252 QIcon icon(result); |
|
253 |
|
254 setData(index, icon, Qt::DecorationRole); |
|
255 m_icons().insert(iconNum, icon); |
|
256 } |
|
257 |
|
258 if (index.data(Ignore).toBool()) |
|
259 setData(index, QColor(Qt::gray), Qt::ForegroundRole); |
|
260 else if (index.data(Friend).toBool()) |
|
261 setData(index, QColor(Qt::green), Qt::ForegroundRole); |
|
262 else |
|
263 setData(index, QBrush(QColor(0xff, 0xcc, 0x00)), Qt::ForegroundRole); |
|
264 } |
|
265 |
|
266 QHash<quint32, QIcon> &PlayersListModel::m_icons() { |
|
267 static QHash<quint32, QIcon> iconsCache; |
|
268 |
|
269 return iconsCache; |
|
270 } |
|
271 |
|
272 void PlayersListModel::updateSortData(const QModelIndex &index) { |
|
273 QString result = |
|
274 QString("%1%2%3%4%5%6") |
|
275 // room admins go first, then server admins, then friends |
|
276 .arg(1 - index.data(RoomAdmin).toInt()) |
|
277 .arg(1 - index.data(ServerAdmin).toInt()) |
|
278 .arg(1 - index.data(Friend).toInt()) |
|
279 // ignored at bottom |
|
280 .arg(index.data(Ignore).toInt()) |
|
281 // keep nicknames starting from non-letter character at bottom within |
|
282 // group assume there are no empty nicks in list |
|
283 .arg(index.data(Qt::DisplayRole).toString().at(0).isLetter() ? 0 : 1) |
|
284 // sort ignoring case |
|
285 .arg(index.data(Qt::DisplayRole).toString().toLower()); |
|
286 |
|
287 setData(index, result, SortRole); |
|
288 } |
|
289 |
|
290 void PlayersListModel::setNickname(const QString &nickname) { |
|
291 m_nickname = nickname; |
|
292 |
|
293 // FIXME: set proper file names |
|
294 // loadSet(m_friendsSet, "friends"); |
|
295 // loadSet(m_ignoredSet, "ignore"); |
|
296 |
|
297 for (int i = rowCount() - 1; i >= 0; --i) checkFriendIgnore(index(i)); |
|
298 } |
|
299 |
|
300 bool PlayersListModel::isFriend(const QString &nickname) { |
|
301 return m_friendsSet.contains(nickname.toLower()); |
|
302 } |
|
303 |
|
304 bool PlayersListModel::isIgnored(const QString &nickname) { |
|
305 return m_ignoredSet.contains(nickname.toLower()); |
|
306 } |
|
307 |
|
308 void PlayersListModel::checkFriendIgnore(const QModelIndex &mi) { |
|
309 setData(mi, isFriend(mi.data().toString()), Friend); |
|
310 setData(mi, isIgnored(mi.data().toString()), Ignore); |
|
311 |
|
312 updateIcon(mi); |
|
313 updateSortData(mi); |
|
314 } |
|
315 |
|
316 void PlayersListModel::loadSet(QSet<QString> &set, const QString &fileName) { |
|
317 set.clear(); |
|
318 |
|
319 QFile txt(fileName); |
|
320 if (!txt.open(QIODevice::ReadOnly)) return; |
|
321 |
|
322 QTextStream stream(&txt); |
|
323 stream.setCodec("UTF-8"); |
|
324 |
|
325 while (!stream.atEnd()) { |
|
326 QString str = stream.readLine(); |
|
327 if (str.startsWith(";") || str.isEmpty()) continue; |
|
328 |
|
329 set.insert(str.trimmed()); |
|
330 } |
|
331 |
|
332 txt.close(); |
|
333 } |
|
334 |
|
335 void PlayersListModel::saveSet(const QSet<QString> &set, |
|
336 const QString &fileName) { |
|
337 qDebug("saving set"); |
|
338 |
|
339 QFile txt(fileName); |
|
340 |
|
341 // list empty? => rather have no file for the list than an empty one |
|
342 if (set.isEmpty()) { |
|
343 if (txt.exists()) { |
|
344 // try to remove file, if successful we're done here. |
|
345 if (txt.remove()) return; |
|
346 } else |
|
347 // there is no file |
|
348 return; |
|
349 } |
|
350 |
|
351 if (!txt.open(QIODevice::WriteOnly | QIODevice::Truncate)) return; |
|
352 |
|
353 QTextStream stream(&txt); |
|
354 stream.setCodec("UTF-8"); |
|
355 |
|
356 stream << "; this list is used by Hedgewars - do not edit it unless you know " |
|
357 "what you're doing!" |
|
358 << endl; |
|
359 |
|
360 foreach (const QString &nick, set.values()) |
|
361 stream << nick << endl; |
|
362 |
|
363 txt.close(); |
|
364 } |