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  QPixmap flag()
48  {
49  IP2C *ip2c = IP2C::instance();
50  const IP2CCountry countryInfo = ip2c->countryInfoForServer(*server);
51  return (countryInfo.isValid() && countryInfo.flag != nullptr) ?
52  *countryInfo.flag : ip2c->flagUnknown;
53  }
54 };
55 
56 DPointered(ServerListRowHandler)
57 
59  int rowIndex, const ServerPtr &server)
60  : model(parentModel), row(rowIndex)
61 {
62  d->server = server;
63 }
64 
65 ServerListRowHandler::ServerListRowHandler(ServerListModel *parentModel, int rowIndex)
66  : model(parentModel), row(rowIndex)
67 {
68  d->server = serverFromList(parentModel, rowIndex);
69 }
70 
71 ServerListRowHandler::~ServerListRowHandler()
72 {
73 }
74 
76 {
77  for (int i = 0; i < NUM_SERVERLIST_COLUMNS; ++i)
78  {
79  if (!ServerListColumns::isColumnVital(i))
80  emptyItem(item(i));
81  }
82 }
83 
84 void ServerListRowHandler::emptyItem(QStandardItem *item)
85 {
86  item->setData("", Qt::DisplayRole);
87  item->setData(QVariant(), Qt::DecorationRole);
88  item->setData(QVariant(), ServerListModel::SLDT_SORT);
89 }
90 
91 QStringList ServerListRowHandler::extractValidGameCVarNames(const QList<GameCVar> &cvars)
92 {
93  QStringList result;
94  for (const GameCVar &cvar : cvars)
95  {
96  if (!cvar.isValid())
97  result << cvar.name();
98  }
99  return result;
100 }
101 
102 void ServerListRowHandler::fillAddressColumn()
103 {
104  QStandardItem *pItem = item(IDAddress);
105  fillItem(pItem, d->server->address(), d->server->hostName());
106 }
107 
108 void ServerListRowHandler::fillItem(QStandardItem *item, const QString &str)
109 {
110  QString strLowcase = str.toLower();
111  item->setData(str, Qt::DisplayRole);
112  item->setData(strLowcase, ServerListModel::SLDT_SORT);
113 }
114 
115 void ServerListRowHandler::fillItem(QStandardItem *item, int sort, const QString &str)
116 {
117  QVariant var = sort;
118 
119  fillItem(item, str);
120  item->setData(sort, ServerListModel::SLDT_SORT);
121 }
122 
123 void ServerListRowHandler::fillItem(QStandardItem *item, int num)
124 {
125  QVariant var = num;
126 
127  item->setData(var, Qt::DisplayRole);
128  item->setData(var, ServerListModel::SLDT_SORT);
129 }
130 
131 void ServerListRowHandler::fillItem(QStandardItem *item, const QHostAddress &addr, const QString &actualDisplay)
132 {
133  QVariant var = addr.toIPv4Address();
134 
135  if (actualDisplay.isEmpty())
136  item->setData(addr.toString(), Qt::DisplayRole);
137  else
138  item->setData(actualDisplay, Qt::DisplayRole);
139  item->setData(var, ServerListModel::SLDT_SORT);
140 }
141 
142 void ServerListRowHandler::fillItem(QStandardItem *item, const QString &sort, const QPixmap &icon)
143 {
144  item->setIcon(QIcon(icon));
145  item->setData(sort, ServerListModel::SLDT_SORT);
146 }
147 
148 void ServerListRowHandler::fillItem(QStandardItem *item, int sort, const QPixmap &image)
149 {
150  item->setData(image, Qt::DecorationRole);
151  item->setData(sort, ServerListModel::SLDT_SORT);
152 }
153 
154 void ServerListRowHandler::fillPlayerColumn()
155 {
156  QStandardItem *pItem = item(IDPlayers);
157 
158  QString style = gConfig.doomseeker.slotStyle;
159  bool botsAreNotPlayers = gConfig.doomseeker.bBotsAreNotPlayers;
160 
161  const PlayersList &players = d->server->players();
162  int sortValue = 0;
163 
164  if (botsAreNotPlayers)
165  sortValue = players.numClientsWithoutBots();
166  else
167  sortValue = players.numClients();
168 
169  if (!PlayersDiagram::isNumericStyle(style))
170  fillItem(pItem, sortValue, PlayersDiagram(d->server).pixmap());
171  else
172  {
173  fillItem(pItem, sortValue, QString("%1/%2").arg(players.numClients())
174  .arg(d->server->maxClients()));
175  }
176 
177  // Unset some data if it has been set before.
178  pItem->setData(QVariant(QVariant::Invalid), PlayersDiagram::isNumericStyle(style) ? Qt::DecorationRole : Qt::DisplayRole);
179  pItem->setData(PlayersDiagram::isNumericStyle(style) ? 0 : USERROLE_RIGHTALIGNDECORATION, Qt::UserRole);
180 }
181 
182 void ServerListRowHandler::fillPortIconColumn()
183 {
184  QStandardItem *pItem = item(IDPort);
185  QPixmap icon = d->server->icon();
186  if (d->server->isKnown())
187  {
188  if (d->server->isLockedAnywhere()) // Draw a key if it is locked.
189  {
190  QPainter iconPainter(&icon);
191  iconPainter.drawPixmap(0, 0, QPixmap(":/locked.png"));
192  iconPainter.end();
193  }
194  else if (d->server->isSecure())
195  {
196  QPainter iconPainter(&icon);
197  iconPainter.drawPixmap(0, 0, QPixmap(":/shield.png"));
198  iconPainter.end();
199  }
200  // 't' is drawn on a different part of the logo therefore it can be
201  // drawn together with other icons
202  if (d->server->isTestingServer())
203  {
204  QPainter iconPainter(&icon);
205  iconPainter.drawPixmap(0, 0, QPixmap(":/t.png"));
206  iconPainter.end();
207  }
208  }
209  fillItem(pItem, d->server->metaObject()->className(), icon);
210 }
211 
212 void ServerListRowHandler::fillServerPointer()
213 {
214  QStandardItem *pItem = item(COL_META);
215  QVariant savePointer = QVariant::fromValue(d->server);
216  pItem->setData(savePointer, ServerListModel::SLDT_POINTER_TO_SERVER_STRUCTURE);
217 }
218 
219 QStandardItem *ServerListRowHandler::item(int columnIndex)
220 {
221  return model->item(row, columnIndex);
222 }
223 
224 void ServerListRowHandler::redraw()
225 {
226  updateServer();
227 
228  // Since updateServer doesn't do anything with the flags we need to
229  // explicitly redraw it here.
230  setCountryFlag();
231 }
232 
233 ServerPtr ServerListRowHandler::server()
234 {
235  return d->server;
236 }
237 
238 void ServerListRowHandler::setBackgroundColor()
239 {
240  QString color;
241  if (d->server->isCustom())
242  color = gConfig.doomseeker.customServersColor;
243  else if (d->server->isLan())
244  color = gConfig.doomseeker.lanServersColor;
245  else if (gConfig.doomseeker.bMarkServersWithBuddies &&
246  gApp->mainWindow() != nullptr &&
247  gApp->mainWindow()->buddiesList()->hasBuddy(d->server))
248  {
249  color = gConfig.doomseeker.buddyServersColor;
250  }
251 
252  for (int column = 0; column < NUM_SERVERLIST_COLUMNS; ++column)
253  {
254  QBrush brush = !color.isEmpty()
255  ? QBrush(QColor(color))
256  : Qt::NoBrush;
257  QStandardItem *pItem = item(column);
258  pItem->setBackground(brush);
259  }
260 }
261 
262 void ServerListRowHandler::setBad()
263 {
264  QStandardItem *qstdItem;
265 
267 
268  qstdItem = item(IDServerName);
269  fillItem(qstdItem, tr("<ERROR>"));
270 
271  item(COL_META)->setData(SGBad, ServerListModel::SLDT_SERVER_GROUP);
272 }
273 
274 void ServerListRowHandler::setBanned()
275 {
276  QStandardItem *qstdItem;
277 
279 
280  qstdItem = item(IDServerName);
281  fillItem(qstdItem, tr("You are banned from this server!"));
282 
283  item(COL_META)->setData(SGBanned, ServerListModel::SLDT_SERVER_GROUP);
284 }
285 
286 void ServerListRowHandler::setCountryFlag()
287 {
288  item(IDServerName)->setIcon(d->flag());
289 }
290 
291 void ServerListRowHandler::setFirstQuery()
292 {
293  item(COL_META)->setData(SGFirstQuery, ServerListModel::SLDT_SERVER_GROUP);
294 }
295 
296 void ServerListRowHandler::setGood()
297 {
298  QStandardItem *qstdItem;
299  QString strTmp;
300 
301  fillPlayerColumn();
302 
303  qstdItem = item(IDPing);
304  fillItem(qstdItem, d->server->ping());
305 
306  qstdItem = item(IDServerName);
307  fillItem(qstdItem, d->server->name());
308 
309  qstdItem = item(IDIwad);
310  fillItem(qstdItem, d->server->iwad());
311 
312  qstdItem = item(IDMap);
313  fillItem(qstdItem, d->server->map());
314 
315  strTmp.clear();
316  for (const PWad &wad : d->server->wads())
317  {
318  if (wad.isOptional())
319  strTmp += QString("[%1] ").arg(wad.name());
320  else
321  strTmp += wad.name() + " ";
322  }
323  strTmp.chop(1);
324  qstdItem = item(IDWads);
325  fillItem(qstdItem, strTmp);
326 
327  qstdItem = item(IDGametype);
328  QString fullGameModeName = d->server->gameMode().name();
329  QStringList modifierNames = extractValidGameCVarNames(d->server->modifiers());
330  if (!modifierNames.isEmpty())
331  fullGameModeName += QString(" (%1)").arg(modifierNames.join(", "));
332  fillItem(qstdItem, fullGameModeName);
333 
334  item(COL_META)->setData(SGNormal, ServerListModel::SLDT_SERVER_GROUP);
335 }
336 
337 void ServerListRowHandler::setRefreshing()
338 {
339  QStandardItem *qstdItem = item(IDServerName);
340  qstdItem->setText(tr("<REFRESHING>"));
341 }
342 
343 void ServerListRowHandler::setTimeout()
344 {
345  QStandardItem *qstdItem;
346 
348 
349  qstdItem = item(IDServerName);
350  fillItem(qstdItem, tr("<NO RESPONSE>"));
351 
352  item(COL_META)->setData(SGTimeout, ServerListModel::SLDT_SERVER_GROUP);
353 }
354 
355 void ServerListRowHandler::setWait()
356 {
357  QStandardItem *qstdItem;
358 
360 
361  qstdItem = item(IDServerName);
362  fillItem(qstdItem, tr("<Refreshed too soon, wait a while and try again>"));
363 
364  item(COL_META)->setData(SGWait, ServerListModel::SLDT_SERVER_GROUP);
365 }
366 
367 ServerPtr ServerListRowHandler::serverFromList(const ServerListModel *parentModel, int rowIndex)
368 {
369  QStandardItem *pItem = parentModel->item(rowIndex, COL_META);
370  if (pItem == nullptr)
371  return ServerPtr();
372  QVariant pointer = pItem->data(ServerListModel::SLDT_POINTER_TO_SERVER_STRUCTURE);
373  if (!pointer.isValid())
374  return ServerPtr();
375  return pointer.value<ServerPtr>();
376 }
377 
378 int ServerListRowHandler::updateServer()
379 {
380  fillServerPointer();
381  fillPortIconColumn();
382  fillAddressColumn();
383 
384  switch (d->server->lastResponse())
385  {
387  setBad();
388  break;
389 
391  setBanned();
392  break;
393 
395  setGood();
396  break;
397 
399  if (d->server->isKnown())
400  setGood();
401  else
402  setWait();
403  break;
404 
406  setTimeout();
407  break;
408 
410  setFirstQuery();
411  if (d->server->isRefreshing())
412  setRefreshing();
413  break;
414 
415  default:
416  gLog << tr("Unknown server response (%1): %2:%3").arg(d->server->lastResponse())
417  .arg(d->server->address().toString()).arg(d->server->port());
418  break;
419  }
420 
421  setBackgroundColor();
422 
423  return row;
424 }