serverlistrowhandler.cpp
1 //------------------------------------------------------------------------------
2 // serverlistrowhandler.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program 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
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; 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 "serverlistrowhandler.h"
24 #include "serverlistcolumn.h"
25 #include "serverlistmodel.h"
26 #include "configuration/doomseekerconfig.h"
27 #include "gui/helpers/playersdiagram.h"
28 #include "gui/widgets/serverlistview.h"
29 #include "gui/dockBuddiesList.h"
30 #include "gui/mainwindow.h"
31 #include "gui/serverlist.h"
32 #include "ip2c/ip2c.h"
33 #include "serverapi/playerslist.h"
34 #include "serverapi/server.h"
36 #include "application.h"
37 #include "log.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  {
73  emptyItem(item(i));
74  }
75  }
76 }
77 
78 void ServerListRowHandler::emptyItem(QStandardItem* item)
79 {
80  item->setData("", Qt::DisplayRole);
81  item->setData(QVariant(), Qt::DecorationRole);
82  item->setData(QVariant(), DTSort);
83 }
84 
85 QStringList ServerListRowHandler::extractValidGameCVarNames(const QList<GameCVar> &cvars)
86 {
87  QStringList result;
88  foreach (const GameCVar &cvar, cvars)
89  {
90  if (!cvar.isValid())
91  {
92  result << cvar.name();
93  }
94  }
95  return result;
96 }
97 
98 void ServerListRowHandler::fillAddressColumn()
99 {
100  QStandardItem* pItem = item(IDAddress);
101  fillItem(pItem, d->server->address(), d->server->hostName() );
102 }
103 
104 void ServerListRowHandler::fillItem(QStandardItem* item, const QString& str)
105 {
106  QString strLowcase = str.toLower();
107  item->setData(str, Qt::DisplayRole);
108  item->setData(strLowcase, DTSort);
109 }
110 
111 void ServerListRowHandler::fillItem(QStandardItem* item, int sort, const QString& str)
112 {
113  QVariant var = sort;
114 
115  fillItem(item, str);
116  item->setData(sort, DTSort);
117 }
118 
119 void ServerListRowHandler::fillItem(QStandardItem* item, int num)
120 {
121  QVariant var = num;
122 
123  item->setData(var, Qt::DisplayRole);
124  item->setData(var, DTSort);
125 }
126 
127 void ServerListRowHandler::fillItem(QStandardItem* item, const QHostAddress& addr, const QString& actualDisplay)
128 {
129  QVariant var = addr.toIPv4Address();
130 
131  if (actualDisplay.isEmpty())
132  {
133  item->setData(addr.toString(), Qt::DisplayRole);
134  }
135  else
136  {
137  item->setData(actualDisplay, Qt::DisplayRole);
138  }
139  item->setData(var, DTSort);
140 }
141 
142 void ServerListRowHandler::fillItem(QStandardItem* item, const QString& sort, const QPixmap& icon)
143 {
144  item->setIcon(QIcon(icon));
145  item->setData(sort, DTSort);
146 }
147 
148 void ServerListRowHandler::fillItem(QStandardItem* item, int sort, const QPixmap& image)
149 {
150  item->setData(image, Qt::DecorationRole);
151  item->setData(sort, DTSort);
152 }
153 
154 void ServerListRowHandler::fillPlayerColumn()
155 {
156  QStandardItem* pItem = item(IDPlayers);
157 
158  int 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  {
166  sortValue = players.numClientsWithoutBots();
167  }
168  else
169  {
170  sortValue = players.numClients();
171  }
172 
173  if(style != NUM_SLOTSTYLES)
174  {
175  fillItem(pItem, sortValue, PlayersDiagram(d->server).pixmap());
176  }
177  else
178  {
179  fillItem(pItem, sortValue, QString("%1/%2").arg(players.numClients())
180  .arg(d->server->numTotalSlots()));
181  }
182 
183  // Unset some data if it has been set before.
184  pItem->setData(QVariant(QVariant::Invalid), style == NUM_SLOTSTYLES ? Qt::DecorationRole : Qt::DisplayRole);
185  pItem->setData(style == NUM_SLOTSTYLES ? 0 : USERROLE_RIGHTALIGNDECORATION, Qt::UserRole);
186 }
187 
188 void ServerListRowHandler::fillPortIconColumn()
189 {
190  QStandardItem* pItem = item(IDPort);
191  QPixmap icon = d->server->icon();
192  if(d->server->isKnown())
193  {
194  if(d->server->isLockedAnywhere()) // Draw a key if it is locked.
195  {
196  QPainter iconPainter(&icon);
197  iconPainter.drawPixmap(0, 0, QPixmap(":/locked.png"));
198  iconPainter.end();
199  }
200  else if(d->server->isSecure())
201  {
202  QPainter iconPainter(&icon);
203  iconPainter.drawPixmap(0, 0, QPixmap(":/shield.png"));
204  iconPainter.end();
205  }
206  }
207  fillItem(pItem, d->server->metaObject()->className(), icon);
208 }
209 
210 void ServerListRowHandler::fillServerPointerColumn()
211 {
212  QStandardItem* pItem = item(IDHiddenServerPointer);
213  QVariant savePointer = qVariantFromValue(d->server);
214  pItem->setData(savePointer, DTPointerToServerStructure);
215 }
216 
217 QStandardItem* ServerListRowHandler::item(int columnIndex)
218 {
219  return model->item(row, columnIndex);
220 }
221 
222 void ServerListRowHandler::redraw()
223 {
224  updateServer(d->server->lastResponse());
225 
226  // Since updateServer doesn't do anything with the flags we need to
227  // explicitly redraw it here.
228  setCountryFlag();
229 }
230 
231 ServerPtr ServerListRowHandler::server()
232 {
233  return d->server;
234 }
235 
236 void ServerListRowHandler::setBackgroundColor()
237 {
238  QString color;
239  if (d->server->isCustom())
240  {
241  color = gConfig.doomseeker.customServersColor;
242  }
243  else if (gApp->mainWindow()->buddiesList()->hasBuddy(d->server))
244  {
245  if (gConfig.doomseeker.bMarkServersWithBuddies)
246  {
247  color = gConfig.doomseeker.buddyServersColor;
248  }
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  qstdItem = item(IDHiddenGroup);
271  fillItem(qstdItem, SGBad);
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  qstdItem = item(IDHiddenGroup);
284  fillItem(qstdItem, SGBanned);
285 }
286 
287 void ServerListRowHandler::setCountryFlag()
288 {
289  QStandardItem* pItem = item(IDServerName);
290 
291  if (!IP2C::instance()->isDataAccessLocked())
292  {
293  IP2CCountryInfo countryInfo = IP2C::instance()->obtainCountryInfo(d->server->address());
294  if (countryInfo.isValid())
295  {
296  QPixmap flag = *countryInfo.flag;
297  pItem->setIcon(flag);
298  }
299  }
300 }
301 
302 void ServerListRowHandler::setFirstQuery()
303 {
304  QStandardItem* qstdItem = item(IDHiddenGroup);
305  fillItem(qstdItem, SGFirstQuery);
306 }
307 
308 void ServerListRowHandler::setGood()
309 {
310  QStandardItem* qstdItem;
311  QString strTmp;
312 
313  fillPlayerColumn();
314 
315  qstdItem = item(IDPing);
316  fillItem(qstdItem, d->server->ping());
317 
318  qstdItem = item(IDServerName);
319  fillItem(qstdItem, d->server->name());
320 
321  qstdItem = item(IDIwad);
322  fillItem(qstdItem, d->server->iwad());
323 
324  qstdItem = item(IDMap);
325  fillItem(qstdItem, d->server->map());
326 
327  strTmp.clear();
328  foreach(const PWad &wad, d->server->wads())
329  {
330  if(wad.isOptional())
331  {
332  strTmp += QString("[%1] ").arg(wad.name());
333  }
334  else
335  {
336  strTmp += wad.name() + " ";
337  }
338  }
339  strTmp.chop(1);
340  qstdItem = item(IDWads);
341  fillItem(qstdItem, strTmp);
342 
343  qstdItem = item(IDGametype);
344  QString fullGameModeName = d->server->gameMode().name();
345  QStringList modifierNames = extractValidGameCVarNames(d->server->modifiers());
346  if (!modifierNames.isEmpty())
347  {
348  fullGameModeName += QString(" (%1)").arg(modifierNames.join(", "));
349  }
350  fillItem(qstdItem, fullGameModeName);
351 
352  qstdItem = item(IDHiddenGroup);
353  fillItem(qstdItem, SGNormal);
354 }
355 
356 void ServerListRowHandler::setRefreshing()
357 {
358  QStandardItem* qstdItem = item(IDServerName);
359  qstdItem->setText(tr("<REFRESHING>"));
360 }
361 
362 void ServerListRowHandler::setTimeout()
363 {
364  QStandardItem* qstdItem;
365 
367 
368  qstdItem = item(IDServerName);
369  fillItem(qstdItem, tr("<NO RESPONSE>"));
370 
371  qstdItem = item(IDHiddenGroup);
372  fillItem(qstdItem, SGTimeout);
373 }
374 
375 void ServerListRowHandler::setWait()
376 {
377  QStandardItem* qstdItem;
378 
380 
381  qstdItem = item(IDServerName);
382  fillItem(qstdItem, tr("<Refreshed too soon, wait a while and try again>") );
383 
384  qstdItem = item(IDHiddenGroup);
385  fillItem(qstdItem, SGWait);
386 }
387 
388 ServerPtr ServerListRowHandler::serverFromList(ServerListModel* parentModel, int rowIndex)
389 {
390  QStandardItem* pItem = parentModel->item(rowIndex, IDHiddenServerPointer);
391  QVariant pointer = qVariantFromValue(pItem->data(DTPointerToServerStructure));
392  if (!pointer.isValid())
393  {
394  return ServerPtr();
395  }
396  return qVariantValue<ServerPtr>(pointer);
397 }
398 
399 //ServerListRowHandler::ServerGroup ServerListRowHandler::serverGroup()
400 //{
401 // QStandardItem* qstdItem = item(row, SLCID_HIDDEN_GROUP);
402 // return static_cast<ServerListRowHandler::ServerGroup>(qstdItem->data(DTSort).toInt());
403 //}
404 
405 int ServerListRowHandler::updateServer(int response)
406 {
407  fillServerPointerColumn();
408  fillPortIconColumn();
409  fillAddressColumn();
410 
411  switch(response)
412  {
414  setBad();
415  break;
416 
418  setBanned();
419  break;
420 
422  setGood();
423  break;
424 
426  if (d->server->isKnown())
427  {
428  setGood();
429  }
430  else
431  {
432  setWait();
433  }
434  break;
435 
437  setTimeout();
438  break;
439 
441  setFirstQuery();
442  if (d->server->isRefreshing())
443  {
444  setRefreshing();
445  }
446  break;
447 
448  default:
449  gLog << tr("Unkown server response (%1): %2:%3").arg(response)
450  .arg(d->server->address().toString()).arg(d->server->port());
451  break;
452  }
453 
454  setBackgroundColor();
455 
456  return row;
457 }
const QString & name() const
Nice name to display to user in Create Game dialog and in other widgets.
IP2CCountryInfo obtainCountryInfo(unsigned int ipaddress)
Definition: ip2c.cpp:152
PWAD hosted on a server.
bool isValid() const
'Null' objects are invalid.
Player is banned from that server.
Definition: server.h:132
"Dummy" response for servers that weren't refreshed yet.
Definition: server.h:136
Response from the server was erroreneous.
Definition: server.h:128
Response was parsed properly and Server information is available.
Definition: server.h:113
Flag and name of the country.
bool isOptional() const
Is this WAD required to join the server?
Server didn't respond at all.
Definition: server.h:117
int numClients() const
Overall number of people and bots on the server.
A general game setting or variable (like fraglimit).
Server responded with "wait", may happen when we refresh too quickly.
Definition: server.h:122
const QString & name() const
File name of the WAD.