serverlistrowhandler.cpp
1 //------------------------------------------------------------------------------
2 // serverlistrowhandler.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2010 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "application.h"
24 #include "configuration/doomseekerconfig.h"
25 #include "gui/dockBuddiesList.h"
26 #include "gui/helpers/playersdiagram.h"
27 #include "gui/mainwindow.h"
28 #include "gui/serverlist.h"
29 #include "gui/widgets/serverlistview.h"
30 #include "ip2c/ip2c.h"
31 #include "log.h"
32 #include "serverapi/playerslist.h"
33 #include "serverapi/server.h"
35 #include "serverlistcolumn.h"
36 #include "serverlistmodel.h"
37 #include "serverlistrowhandler.h"
38 #include <QPainter>
39 
40 using namespace ServerListColumnId;
41 
42 DClass<ServerListRowHandler>
43 {
44 public:
45  ServerPtr server;
46 };
47 
48 DPointered(ServerListRowHandler)
49 
51  int rowIndex, const ServerPtr &server)
52  : model(parentModel), row(rowIndex)
53 {
54  d->server = server;
55 }
56 
57 ServerListRowHandler::ServerListRowHandler(ServerListModel *parentModel, int rowIndex)
58  : model(parentModel), row(rowIndex)
59 {
60  d->server = serverFromList(parentModel, rowIndex);
61 }
62 
63 ServerListRowHandler::~ServerListRowHandler()
64 {
65 }
66 
68 {
69  for (int i = 0; i < NUM_SERVERLIST_COLUMNS; ++i)
70  {
71  if (!ServerListColumns::isColumnVital(i))
72  emptyItem(item(i));
73  }
74 }
75 
76 void ServerListRowHandler::emptyItem(QStandardItem *item)
77 {
78  item->setData("", Qt::DisplayRole);
79  item->setData(QVariant(), Qt::DecorationRole);
80  item->setData(QVariant(), ServerListModel::SLDT_SORT);
81 }
82 
83 QStringList ServerListRowHandler::extractValidGameCVarNames(const QList<GameCVar> &cvars)
84 {
85  QStringList result;
86  for (const GameCVar &cvar : cvars)
87  {
88  if (!cvar.isValid())
89  result << cvar.name();
90  }
91  return result;
92 }
93 
94 void ServerListRowHandler::fillAddressColumn()
95 {
96  QStandardItem *pItem = item(IDAddress);
97  fillItem(pItem, d->server->address(), d->server->hostName());
98 }
99 
100 void ServerListRowHandler::fillItem(QStandardItem *item, const QString &str)
101 {
102  QString strLowcase = str.toLower();
103  item->setData(str, Qt::DisplayRole);
104  item->setData(strLowcase, ServerListModel::SLDT_SORT);
105 }
106 
107 void ServerListRowHandler::fillItem(QStandardItem *item, int sort, const QString &str)
108 {
109  QVariant var = sort;
110 
111  fillItem(item, str);
112  item->setData(sort, ServerListModel::SLDT_SORT);
113 }
114 
115 void ServerListRowHandler::fillItem(QStandardItem *item, int num)
116 {
117  QVariant var = num;
118 
119  item->setData(var, Qt::DisplayRole);
120  item->setData(var, ServerListModel::SLDT_SORT);
121 }
122 
123 void ServerListRowHandler::fillItem(QStandardItem *item, const QHostAddress &addr, const QString &actualDisplay)
124 {
125  QVariant var = addr.toIPv4Address();
126 
127  if (actualDisplay.isEmpty())
128  item->setData(addr.toString(), Qt::DisplayRole);
129  else
130  item->setData(actualDisplay, Qt::DisplayRole);
131  item->setData(var, ServerListModel::SLDT_SORT);
132 }
133 
134 void ServerListRowHandler::fillItem(QStandardItem *item, const QString &sort, const QPixmap &icon)
135 {
136  item->setIcon(QIcon(icon));
137  item->setData(sort, ServerListModel::SLDT_SORT);
138 }
139 
140 void ServerListRowHandler::fillItem(QStandardItem *item, int sort, const QPixmap &image)
141 {
142  item->setData(image, Qt::DecorationRole);
143  item->setData(sort, ServerListModel::SLDT_SORT);
144 }
145 
146 void ServerListRowHandler::fillPlayerColumn()
147 {
148  QStandardItem *pItem = item(IDPlayers);
149 
150  QString style = gConfig.doomseeker.slotStyle;
151  bool botsAreNotPlayers = gConfig.doomseeker.bBotsAreNotPlayers;
152 
153  const PlayersList &players = d->server->players();
154  int sortValue = 0;
155 
156  if (botsAreNotPlayers)
157  sortValue = players.numClientsWithoutBots();
158  else
159  sortValue = players.numClients();
160 
161  if (!PlayersDiagram::isNumericStyle(style))
162  fillItem(pItem, sortValue, PlayersDiagram(d->server).pixmap());
163  else
164  {
165  fillItem(pItem, sortValue, QString("%1/%2").arg(players.numClients())
166  .arg(d->server->numTotalSlots()));
167  }
168 
169  // Unset some data if it has been set before.
170  pItem->setData(QVariant(QVariant::Invalid), PlayersDiagram::isNumericStyle(style) ? Qt::DecorationRole : Qt::DisplayRole);
171  pItem->setData(PlayersDiagram::isNumericStyle(style) ? 0 : USERROLE_RIGHTALIGNDECORATION, Qt::UserRole);
172 }
173 
174 void ServerListRowHandler::fillPortIconColumn()
175 {
176  QStandardItem *pItem = item(IDPort);
177  QPixmap icon = d->server->icon();
178  if (d->server->isKnown())
179  {
180  if (d->server->isLockedAnywhere()) // Draw a key if it is locked.
181  {
182  QPainter iconPainter(&icon);
183  iconPainter.drawPixmap(0, 0, QPixmap(":/locked.png"));
184  iconPainter.end();
185  }
186  else if (d->server->isSecure())
187  {
188  QPainter iconPainter(&icon);
189  iconPainter.drawPixmap(0, 0, QPixmap(":/shield.png"));
190  iconPainter.end();
191  }
192  // 't' is drawn on a different part of the logo therefore it can be
193  // drawn together with other icons
194  if (d->server->isTestingServer())
195  {
196  QPainter iconPainter(&icon);
197  iconPainter.drawPixmap(0, 0, QPixmap(":/t.png"));
198  iconPainter.end();
199  }
200  }
201  fillItem(pItem, d->server->metaObject()->className(), icon);
202 }
203 
204 void ServerListRowHandler::fillServerPointer()
205 {
206  QStandardItem *pItem = item(COL_META);
207  QVariant savePointer = QVariant::fromValue(d->server);
208  pItem->setData(savePointer, ServerListModel::SLDT_POINTER_TO_SERVER_STRUCTURE);
209 }
210 
211 QStandardItem *ServerListRowHandler::item(int columnIndex)
212 {
213  return model->item(row, columnIndex);
214 }
215 
216 void ServerListRowHandler::redraw()
217 {
218  updateServer();
219 
220  // Since updateServer doesn't do anything with the flags we need to
221  // explicitly redraw it here.
222  setCountryFlag();
223 }
224 
225 ServerPtr ServerListRowHandler::server()
226 {
227  return d->server;
228 }
229 
230 void ServerListRowHandler::setBackgroundColor()
231 {
232  QString color;
233  if (d->server->isCustom())
234  color = gConfig.doomseeker.customServersColor;
235  else if (d->server->isLan())
236  color = gConfig.doomseeker.lanServersColor;
237  else if (gApp->mainWindow()->buddiesList()->hasBuddy(d->server))
238  {
239  if (gConfig.doomseeker.bMarkServersWithBuddies)
240  color = gConfig.doomseeker.buddyServersColor;
241  }
242 
243  for (int column = 0; column < NUM_SERVERLIST_COLUMNS; ++column)
244  {
245  QBrush brush = !color.isEmpty()
246  ? QBrush(QColor(color))
247  : Qt::NoBrush;
248  QStandardItem *pItem = item(column);
249  pItem->setBackground(brush);
250  }
251 }
252 
253 void ServerListRowHandler::setBad()
254 {
255  QStandardItem *qstdItem;
256 
258 
259  qstdItem = item(IDServerName);
260  fillItem(qstdItem, tr("<ERROR>"));
261 
262  item(COL_META)->setData(SGBad, ServerListModel::SLDT_SERVER_GROUP);
263 }
264 
265 void ServerListRowHandler::setBanned()
266 {
267  QStandardItem *qstdItem;
268 
270 
271  qstdItem = item(IDServerName);
272  fillItem(qstdItem, tr("You are banned from this server!"));
273 
274  item(COL_META)->setData(SGBanned, ServerListModel::SLDT_SERVER_GROUP);
275 }
276 
277 void ServerListRowHandler::setCountryFlag()
278 {
279  QStandardItem *pItem = item(IDServerName);
280 
281  if (!IP2C::instance()->isDataAccessLocked())
282  {
283  IP2CCountryInfo countryInfo = IP2C::instance()->obtainCountryInfo(d->server->address());
284  if (countryInfo.isValid())
285  {
286  QPixmap flag = *countryInfo.flag;
287  pItem->setIcon(flag);
288  }
289  }
290 }
291 
292 void ServerListRowHandler::setFirstQuery()
293 {
294  item(COL_META)->setData(SGFirstQuery, ServerListModel::SLDT_SERVER_GROUP);
295 }
296 
297 void ServerListRowHandler::setGood()
298 {
299  QStandardItem *qstdItem;
300  QString strTmp;
301 
302  fillPlayerColumn();
303 
304  qstdItem = item(IDPing);
305  fillItem(qstdItem, d->server->ping());
306 
307  qstdItem = item(IDServerName);
308  fillItem(qstdItem, d->server->name());
309 
310  qstdItem = item(IDIwad);
311  fillItem(qstdItem, d->server->iwad());
312 
313  qstdItem = item(IDMap);
314  fillItem(qstdItem, d->server->map());
315 
316  strTmp.clear();
317  for (const PWad &wad : d->server->wads())
318  {
319  if (wad.isOptional())
320  strTmp += QString("[%1] ").arg(wad.name());
321  else
322  strTmp += wad.name() + " ";
323  }
324  strTmp.chop(1);
325  qstdItem = item(IDWads);
326  fillItem(qstdItem, strTmp);
327 
328  qstdItem = item(IDGametype);
329  QString fullGameModeName = d->server->gameMode().name();
330  QStringList modifierNames = extractValidGameCVarNames(d->server->modifiers());
331  if (!modifierNames.isEmpty())
332  fullGameModeName += QString(" (%1)").arg(modifierNames.join(", "));
333  fillItem(qstdItem, fullGameModeName);
334 
335  item(COL_META)->setData(SGNormal, ServerListModel::SLDT_SERVER_GROUP);
336 }
337 
338 void ServerListRowHandler::setRefreshing()
339 {
340  QStandardItem *qstdItem = item(IDServerName);
341  qstdItem->setText(tr("<REFRESHING>"));
342 }
343 
344 void ServerListRowHandler::setTimeout()
345 {
346  QStandardItem *qstdItem;
347 
349 
350  qstdItem = item(IDServerName);
351  fillItem(qstdItem, tr("<NO RESPONSE>"));
352 
353  item(COL_META)->setData(SGTimeout, ServerListModel::SLDT_SERVER_GROUP);
354 }
355 
356 void ServerListRowHandler::setWait()
357 {
358  QStandardItem *qstdItem;
359 
361 
362  qstdItem = item(IDServerName);
363  fillItem(qstdItem, tr("<Refreshed too soon, wait a while and try again>"));
364 
365  item(COL_META)->setData(SGWait, ServerListModel::SLDT_SERVER_GROUP);
366 }
367 
368 ServerPtr ServerListRowHandler::serverFromList(const ServerListModel *parentModel, int rowIndex)
369 {
370  QStandardItem *pItem = parentModel->item(rowIndex, COL_META);
371  QVariant pointer = pItem->data(ServerListModel::SLDT_POINTER_TO_SERVER_STRUCTURE);
372  if (!pointer.isValid())
373  return ServerPtr();
374  return pointer.value<ServerPtr>();
375 }
376 
377 int ServerListRowHandler::updateServer()
378 {
379  fillServerPointer();
380  fillPortIconColumn();
381  fillAddressColumn();
382 
383  switch (d->server->lastResponse())
384  {
386  setBad();
387  break;
388 
390  setBanned();
391  break;
392 
394  setGood();
395  break;
396 
398  if (d->server->isKnown())
399  setGood();
400  else
401  setWait();
402  break;
403 
405  setTimeout();
406  break;
407 
409  setFirstQuery();
410  if (d->server->isRefreshing())
411  setRefreshing();
412  break;
413 
414  default:
415  gLog << tr("Unknown server response (%1): %2:%3").arg(d->server->lastResponse())
416  .arg(d->server->address().toString()).arg(d->server->port());
417  break;
418  }
419 
420  setBackgroundColor();
421 
422  return row;
423 }