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->numTotalSlots()));
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 (gApp->mainWindow()->buddiesList()->hasBuddy(d->server))
246  {
247  if (gConfig.doomseeker.bMarkServersWithBuddies)
248  color = gConfig.doomseeker.buddyServersColor;
249  }
250 
251  for (int column = 0; column < NUM_SERVERLIST_COLUMNS; ++column)
252  {
253  QBrush brush = !color.isEmpty()
254  ? QBrush(QColor(color))
255  : Qt::NoBrush;
256  QStandardItem *pItem = item(column);
257  pItem->setBackground(brush);
258  }
259 }
260 
261 void ServerListRowHandler::setBad()
262 {
263  QStandardItem *qstdItem;
264 
266 
267  qstdItem = item(IDServerName);
268  fillItem(qstdItem, tr("<ERROR>"));
269 
270  item(COL_META)->setData(SGBad, ServerListModel::SLDT_SERVER_GROUP);
271 }
272 
273 void ServerListRowHandler::setBanned()
274 {
275  QStandardItem *qstdItem;
276 
278 
279  qstdItem = item(IDServerName);
280  fillItem(qstdItem, tr("You are banned from this server!"));
281 
282  item(COL_META)->setData(SGBanned, ServerListModel::SLDT_SERVER_GROUP);
283 }
284 
285 void ServerListRowHandler::setCountryFlag()
286 {
287  item(IDServerName)->setIcon(d->flag());
288 }
289 
290 void ServerListRowHandler::setFirstQuery()
291 {
292  item(COL_META)->setData(SGFirstQuery, ServerListModel::SLDT_SERVER_GROUP);
293 }
294 
295 void ServerListRowHandler::setGood()
296 {
297  QStandardItem *qstdItem;
298  QString strTmp;
299 
300  fillPlayerColumn();
301 
302  qstdItem = item(IDPing);
303  fillItem(qstdItem, d->server->ping());
304 
305  qstdItem = item(IDServerName);
306  fillItem(qstdItem, d->server->name());
307 
308  qstdItem = item(IDIwad);
309  fillItem(qstdItem, d->server->iwad());
310 
311  qstdItem = item(IDMap);
312  fillItem(qstdItem, d->server->map());
313 
314  strTmp.clear();
315  for (const PWad &wad : d->server->wads())
316  {
317  if (wad.isOptional())
318  strTmp += QString("[%1] ").arg(wad.name());
319  else
320  strTmp += wad.name() + " ";
321  }
322  strTmp.chop(1);
323  qstdItem = item(IDWads);
324  fillItem(qstdItem, strTmp);
325 
326  qstdItem = item(IDGametype);
327  QString fullGameModeName = d->server->gameMode().name();
328  QStringList modifierNames = extractValidGameCVarNames(d->server->modifiers());
329  if (!modifierNames.isEmpty())
330  fullGameModeName += QString(" (%1)").arg(modifierNames.join(", "));
331  fillItem(qstdItem, fullGameModeName);
332 
333  item(COL_META)->setData(SGNormal, ServerListModel::SLDT_SERVER_GROUP);
334 }
335 
336 void ServerListRowHandler::setRefreshing()
337 {
338  QStandardItem *qstdItem = item(IDServerName);
339  qstdItem->setText(tr("<REFRESHING>"));
340 }
341 
342 void ServerListRowHandler::setTimeout()
343 {
344  QStandardItem *qstdItem;
345 
347 
348  qstdItem = item(IDServerName);
349  fillItem(qstdItem, tr("<NO RESPONSE>"));
350 
351  item(COL_META)->setData(SGTimeout, ServerListModel::SLDT_SERVER_GROUP);
352 }
353 
354 void ServerListRowHandler::setWait()
355 {
356  QStandardItem *qstdItem;
357 
359 
360  qstdItem = item(IDServerName);
361  fillItem(qstdItem, tr("<Refreshed too soon, wait a while and try again>"));
362 
363  item(COL_META)->setData(SGWait, ServerListModel::SLDT_SERVER_GROUP);
364 }
365 
366 ServerPtr ServerListRowHandler::serverFromList(const ServerListModel *parentModel, int rowIndex)
367 {
368  QStandardItem *pItem = parentModel->item(rowIndex, COL_META);
369  QVariant pointer = pItem->data(ServerListModel::SLDT_POINTER_TO_SERVER_STRUCTURE);
370  if (!pointer.isValid())
371  return ServerPtr();
372  return pointer.value<ServerPtr>();
373 }
374 
375 int ServerListRowHandler::updateServer()
376 {
377  fillServerPointer();
378  fillPortIconColumn();
379  fillAddressColumn();
380 
381  switch (d->server->lastResponse())
382  {
384  setBad();
385  break;
386 
388  setBanned();
389  break;
390 
392  setGood();
393  break;
394 
396  if (d->server->isKnown())
397  setGood();
398  else
399  setWait();
400  break;
401 
403  setTimeout();
404  break;
405 
407  setFirstQuery();
408  if (d->server->isRefreshing())
409  setRefreshing();
410  break;
411 
412  default:
413  gLog << tr("Unknown server response (%1): %2:%3").arg(d->server->lastResponse())
414  .arg(d->server->address().toString()).arg(d->server->port());
415  break;
416  }
417 
418  setBackgroundColor();
419 
420  return row;
421 }