serverlist.cpp
1 //------------------------------------------------------------------------------
2 // serverlist.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) 2009 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "configuration/doomseekerconfig.h"
24 #include "gui/remoteconsole.h"
25 #include "gui/serverlist.h"
26 #include "gui/models/serverlistcolumn.h"
27 #include "gui/models/serverlistmodel.h"
28 #include "gui/models/serverlistproxymodel.h"
29 #include "gui/models/serverlistrowhandler.h"
30 #include "gui/widgets/serverlistcontextmenu.h"
31 #include "gui/widgets/serverlistview.h"
32 #include "pathfinder/pathfinder.h"
33 #include "pathfinder/wadpathfinder.h"
34 #include "plugins/engineplugin.h"
35 #include "refresher/refresher.h"
36 #include "serverapi/tooltips/tooltipgenerator.h"
37 #include "serverapi/gameexeretriever.h"
38 #include "serverapi/message.h"
39 #include "serverapi/playerslist.h"
40 #include "serverapi/server.h"
42 #include "urlopener.h"
43 #include <QApplication>
44 #include <QClipboard>
45 #include <QDesktopServices>
46 #include <QHeaderView>
47 #include <QMenu>
48 #include <QMessageBox>
49 #include <QStandardItem>
50 #include <QToolTip>
51 #include <QUrl>
52 
53 const QString ServerListHandler::FONT_COLOR_MISSING = "#ff0000";
54 const QString ServerListHandler::FONT_COLOR_OPTIONAL = "#ff9f00";
55 const QString ServerListHandler::FONT_COLOR_FOUND = "#009f00";
56 
57 using namespace ServerListColumnId;
58 
59 ServerListHandler::ServerListHandler(ServerListView* serverTable, QWidget* pMainWindow)
60 : mainWindow(pMainWindow), model(NULL),
61  sortingProxy(NULL), sortOrder(Qt::AscendingOrder),
62  sortIndex(-1), table(serverTable)
63 {
64  prepareServerTable();
65 
66  needsCleaning = false;
67 
68  initCleanerTimer();
69 }
70 
71 ServerListHandler::~ServerListHandler()
72 {
73  saveColumnsWidthsSettings();
74 }
75 
76 void ServerListHandler::applyFilter(const ServerListFilterInfo& filterInfo)
77 {
78  gConfig.serverFilter.info = filterInfo;
79 
80  ServerListProxyModel* pModel = static_cast<ServerListProxyModel*>(table->model());
81  pModel->setFilterInfo(filterInfo);
82 
83  needsCleaning = true;
84 }
85 
86 bool ServerListHandler::areColumnsWidthsSettingsChanged()
87 {
88  for(int i = 0; i < NUM_SERVERLIST_COLUMNS; ++i)
89  {
90  if(ServerListColumns::columns[i].width != table->columnWidth(i))
91  {
92  return true;
93  }
94  }
95 
96  return false;
97 }
98 
99 void ServerListHandler::clearTable()
100 {
101  model->destroyRows();
102 }
103 
104 void ServerListHandler::cleanUp()
105 {
106  if (needsCleaning && mainWindow->isActiveWindow())
107  {
108  if (sortIndex >= 0)
109  {
110  ServerListProxyModel* pModel = static_cast<ServerListProxyModel*>(table->model());
111  pModel->invalidate();
112  pModel->sortServers(sortIndex, sortOrder);
113  }
114 
116  table->updateAllRows();
117  needsCleaning = false;
118  }
119 }
120 
121 void ServerListHandler::cleanUpForce()
122 {
123  needsCleaning = true;
124  cleanUp();
125 }
126 
127 void ServerListHandler::clearAdditionalSorting()
128 {
129  sortingProxy->clearAdditionalSorting();
130 }
131 
133 {
134  if (isSortingByColumn(index))
135  {
136  sortOrder = swapCurrentSortOrder();
137  }
138  else
139  {
140  sortOrder = getColumnDefaultSortOrder(index);
141  }
142  sortIndex = index;
143 
144  cleanUpForce();
145 
146  QHeaderView* header = table->horizontalHeader();
147  header->setSortIndicator(sortIndex, sortOrder);
148 }
149 
150 void ServerListHandler::connectTableModelProxySlots()
151 {
152  QHeaderView* header = table->horizontalHeader();
153  connect(header, SIGNAL( sectionClicked(int) ), this, SLOT ( columnHeaderClicked(int) ) );
154  connect(model, SIGNAL( modelCleared() ), this, SLOT( modelCleared() ) );
155  connect(table->selectionModel(), SIGNAL( selectionChanged(const QItemSelection&, const QItemSelection&) ), this, SLOT( itemSelected(const QItemSelection&) ));
156  connect(table, SIGNAL( middleMouseClick(const QModelIndex&, const QPoint&) ), this, SLOT( tableMiddleClicked(const QModelIndex&, const QPoint&) ) );
157  connect(table, SIGNAL( rightMouseClick(const QModelIndex&, const QPoint&) ), this, SLOT ( tableRightClicked(const QModelIndex&, const QPoint&)) );
158  connect(table, SIGNAL( entered(const QModelIndex&) ), this, SLOT ( mouseEntered(const QModelIndex&)) );
159  connect(table, SIGNAL( leftMouseDoubleClicked(const QModelIndex&, const QPoint&)), this, SLOT( doubleClicked(const QModelIndex&)) );
160 }
161 
162 void ServerListHandler::contextMenuAboutToHide()
163 {
164  sender()->deleteLater();
165 }
166 
167 void ServerListHandler::contextMenuTriggered(QAction* action)
168 {
169  ServerListContextMenu *contextMenu = static_cast<ServerListContextMenu*>(sender());
170  ServerPtr server = contextMenu->server();
171  // 1. This is a bit convoluted, but emitting the serverFilterModified
172  // signal leads to a call to applyFilter() in this class.
173  // 2. Since the menu modifies existing server filter, the worst that can
174  // happen is that we set the same filter again.
175  emit serverFilterModified(contextMenu->serverFilter());
176 
177  ServerListContextMenu::Result contextMenuResult = contextMenu->translateQMenuResult(action);
178  switch (contextMenuResult)
179  {
181  // Do nothing.
182  break;
183 
184  case ServerListContextMenu::FindMissingWADs:
185  emit findMissingWADs(server);
186  break;
187 
188  case ServerListContextMenu::Join:
189  emit serverDoubleClicked(server);
190  break;
191 
192  case ServerListContextMenu::OpenRemoteConsole:
193  new RemoteConsole(server);
194  break;
195 
196  case ServerListContextMenu::OpenURL:
197  // Calling QDesktopServices::openUrl() here directly resulted
198  // in a crash somewhere in Qt libraries. UrlOpener defers the
199  // call with a timer and this fixes the crash.
200  UrlOpener::instance()->open(server->webSite());
201  break;
202 
204  // Do nothing; ignore.
205  break;
206 
207  case ServerListContextMenu::Refresh:
208  refreshSelected();
209  break;
210 
211  case ServerListContextMenu::ShowJoinCommandLine:
212  emit displayServerJoinCommandLine(server);
213  break;
214 
215  case ServerListContextMenu::SortAdditionallyAscending:
216  sortAdditionally(contextMenu->modelIndex(), Qt::AscendingOrder);
217  break;
218 
219  case ServerListContextMenu::SortAdditionallyDescending:
220  sortAdditionally(contextMenu->modelIndex(), Qt::DescendingOrder);
221  break;
222 
223  case ServerListContextMenu::RemoveAdditionalSortingForColumn:
224  removeAdditionalSortingForColumn(contextMenu->modelIndex());
225  break;
226 
227  case ServerListContextMenu::ClearAdditionalSorting:
228  clearAdditionalSorting();
229  break;
230 
231  default:
232  QMessageBox::warning(mainWindow, tr("Doomseeker - context menu warning"),
233  tr("Unhandled behavior in ServerListHandler::contextMenuTriggered()"));
234  break;
235  }
236 }
237 
238 QString ServerListHandler::createIwadToolTip(ServerPtr server)
239 {
240  if (!server->isKnown())
241  {
242  return QString();
243  }
244 
245  // This will only return anything if we have the "TellMe..." option enabled.
246  bool bFindIwad = gConfig.doomseeker.bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn;
247 
248  if (bFindIwad)
249  {
250  static const QString FORMAT_TEMPLATE = "<font color=\"%1\">%2</font>";
251 
252  Message binMessage;
253  // Use offline binary so that testing builds are not triggered.
254  QString binPath = GameExeRetriever(*server->plugin()->gameExe()).pathToOfflineExe(binMessage);
255 
256  WadFindResult path = findWad(server, server->iwad());
257 
258  if (path.isValid())
259  {
260  QString msg = path.path();
261  if (path.isAlias())
262  {
263  msg += " " + tr("(alias of: %1)").arg(server->iwad());
264  }
265  return FORMAT_TEMPLATE.arg(FONT_COLOR_FOUND, msg);
266  }
267  else
268  {
269  return FORMAT_TEMPLATE.arg(FONT_COLOR_MISSING, tr("MISSING"));
270  }
271  }
272 
273  return QString();
274 }
275 
276 ServerListModel* ServerListHandler::createModel()
277 {
278  ServerListModel* serverListModel = new ServerListModel(this);
279  serverListModel->prepareHeaders();
280 
281  return serverListModel;
282 }
283 
284 QString ServerListHandler::createPlayersToolTip(ServerCPtr server)
285 {
286  if (server == NULL || !server->isKnown())
287  {
288  return QString();
289  }
290 
291  TooltipGenerator* tooltipGenerator = server->tooltipGenerator();
292 
293  QString ret;
294  ret = "<div style='white-space: pre'>";
295  ret += tooltipGenerator->gameInfoTableHTML();
296  if(server->players().numClients() != 0)
297  {
298  ret += tooltipGenerator->playerTableHTML();
299  }
300  ret += "</div>";
301 
302  delete tooltipGenerator;
303  return ret;
304 }
305 
306 QString ServerListHandler::createPortToolTip(ServerCPtr server)
307 {
308  if (server == NULL || !server->isKnown())
309  return QString();
310 
311  QString ret;
312  if (server->isLocked())
313  ret += "Password protected\n";
314  if (server->isLockedInGame())
315  ret += "Password protected in-game\n";
316  if (server->isSecure())
317  ret += "Enforces master bans\n";
318  return ret.trimmed();
319 }
320 
321 QString ServerListHandler::createPwadsToolTip(ServerPtr server)
322 {
323  if (server == NULL || !server->isKnown() || server->numWads() == 0)
324  {
325  return QString();
326  }
327 
328  // Prepare initial formatting.
329  static const QString toolTip = "<div style='white-space: pre'>%1</div>";
330  QString content;
331 
332  const QList<PWad>& pwads = server->wads();
333 
334  // Check if we should seek and colorize.
335  bool bFindWads = gConfig.doomseeker.bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn;
336 
337  // Engage!
338  if (bFindWads)
339  {
340  QStringList pwadsFormatted;
341  foreach (const PWad &wad, pwads)
342  {
343  pwadsFormatted << createPwadToolTipInfo(wad, server);
344  }
345 
346  content = "<table cellspacing=1>";
347  content += pwadsFormatted.join("\n");
348  content += "</table>";
349  }
350  else
351  {
352  foreach (const PWad &wad, pwads)
353  {
354  content += wad.name() + "\n";
355  }
356  content.chop(1); // Get rid of extra \n.
357  }
358 
359  return toolTip.arg(content);
360 }
361 
362 QString ServerListHandler::createPwadToolTipInfo(const PWad& pwad, const ServerPtr &server)
363 {
364  WadFindResult path = findWad(server, pwad.name());
365 
366  QString fontColor = "#777777";
367  QStringList cells;
368 
369  cells << pwad.name();
370  if (path.isValid())
371  {
372  fontColor = FONT_COLOR_FOUND;
373  cells << path.path();
374  }
375  else
376  {
377  if (pwad.isOptional())
378  {
379  fontColor = FONT_COLOR_OPTIONAL;
380  cells << tr("OPTIONAL");
381  }
382  else
383  {
384  fontColor = FONT_COLOR_MISSING;
385  cells << tr("MISSING");
386  }
387  }
388  if (path.isAlias())
389  {
390  cells << tr("ALIAS");
391  }
392  else
393  {
394  cells << "";
395  }
396 
397  QString formattedStringBegin = QString("<tr style=\"color: %1;\">").arg(fontColor);
398  QString formattedStringMiddle;
399  QString space = "";
400  foreach (const QString &cell, cells)
401  {
402  formattedStringMiddle += QString("<td style=\"padding-right: 5;\">%1</td>").arg(cell);
403  space = " ";
404  }
405  return formattedStringBegin + formattedStringMiddle + "</tr>";
406 }
407 
408 QString ServerListHandler::createServerNameToolTip(ServerCPtr server)
409 {
410  if (server == NULL)
411  {
412  return QString();
413  }
414 
415  TooltipGenerator* tooltipGenerator = server->tooltipGenerator();
416 
417  QString ret;
418  QString generalInfo = tooltipGenerator->generalInfoHTML();
419 
420  if (!generalInfo.isEmpty())
421  {
422  ret = "<div style='white-space: pre'>";
423  ret += generalInfo;
424  ret += "</div>";
425  }
426 
427  delete tooltipGenerator;
428  return ret;
429 }
430 
431 ServerListProxyModel *ServerListHandler::createSortingProxy(ServerListModel* serverListModel)
432 {
433  ServerListProxyModel* proxy = new ServerListProxyModel(this);
434  this->connect(proxy, SIGNAL(additionalSortColumnsChanged()),
435  SLOT(updateHeaderTitles()));
436  this->connect(proxy, SIGNAL(additionalSortColumnsChanged()),
437  SLOT(saveAdditionalSortingConfig()));
438  proxy->setSourceModel(serverListModel);
439  proxy->setSortRole(ServerListModel::SLDT_SORT);
440  proxy->setSortCaseSensitivity( Qt::CaseInsensitive );
441  proxy->setFilterKeyColumn(IDServerName);
442 
443  return proxy;
444 }
445 
446 void ServerListHandler::doubleClicked(const QModelIndex& index)
447 {
448  emit serverDoubleClicked(serverFromIndex(index));
449 }
450 
451 WadFindResult ServerListHandler::findWad(ServerPtr server, const QString &name) const
452 {
453  PathFinder pathFinder = server->wadPathFinder();
454  WadPathFinder wadFinder(pathFinder);
455  return wadFinder.find(name);
456 }
457 
458 Qt::SortOrder ServerListHandler::getColumnDefaultSortOrder(int columnId)
459 {
460  // Right now we can assume that columnIndex == columnId.
461  return ServerListColumns::columns[columnId].defaultSortOrder;
462 }
463 
464 bool ServerListHandler::hasAtLeastOneServer() const
465 {
466  return model->rowCount() > 0;
467 }
468 
469 void ServerListHandler::initCleanerTimer()
470 {
471  cleanerTimer.setInterval(200);
472  cleanerTimer.start();
473  connect(&cleanerTimer, SIGNAL( timeout() ), this, SLOT ( cleanUp() ) );
474 }
475 
476 bool ServerListHandler::isAnyColumnSortedAdditionally() const
477 {
478  return sortingProxy->isAnyColumnSortedAdditionally();
479 }
480 
481 bool ServerListHandler::isSortingAdditionallyByColumn(int column) const
482 {
483  return sortingProxy->isSortingAdditionallyByColumn(column);
484 }
485 
486 bool ServerListHandler::isSortingByColumn(int columnIndex)
487 {
488  return sortIndex == columnIndex;
489 }
490 
491 void ServerListHandler::itemSelected(const QItemSelection& selection)
492 {
493  QSortFilterProxyModel* pModel = static_cast<QSortFilterProxyModel*>(table->model());
494  QModelIndexList indexList = selection.indexes();
495 
496  QList<ServerPtr> servers;
497  for(int i = 0; i < indexList.count(); ++i)
498  {
499  QModelIndex realIndex = pModel->mapToSource(indexList[i]);
500  ServerPtr server = model->serverFromList(realIndex);
501  servers.append(server);
502  }
503  emit serversSelected(servers);
504 }
505 
507 {
508  for (int i = 0; i < model->rowCount(); ++i)
509  {
510  ServerPtr server = model->serverFromList(i);
511  server->lookupHost();
512  }
513 }
514 
515 void ServerListHandler::modelCleared()
516 {
517  QList<ServerPtr> servers;
518  emit serversSelected(servers);
519 }
520 
521 void ServerListHandler::mouseEntered(const QModelIndex& index)
522 {
523  QSortFilterProxyModel* pModel = static_cast<QSortFilterProxyModel*>(table->model());
524  QModelIndex realIndex = pModel->mapToSource(index);
525  ServerPtr server = model->serverFromList(realIndex);
526  QString tooltip;
527 
528  // Functions inside cases perform checks on the server structure
529  // to see if any tooltip should be generated. Empty string is returned
530  // in case if it should be not.
531  switch(index.column())
532  {
533  case IDPort:
534  tooltip = createPortToolTip(server);
535  break;
536 
537  case IDAddress:
538  tooltip = server->hostName(true);
539  break;
540 
541  case IDPlayers:
542  tooltip = createPlayersToolTip(server);
543  break;
544 
545  case IDServerName:
546  tooltip = createServerNameToolTip(server);
547  break;
548 
549  case IDIwad:
550  tooltip = createIwadToolTip(server);
551  break;
552 
553  case IDWads:
554  tooltip = createPwadsToolTip(server);
555  break;
556 
557  default:
558  tooltip = "";
559  break;
560  }
561 
562  QToolTip::showText(QCursor::pos(), tooltip, table);
563 }
564 
565 void ServerListHandler::prepareServerTable()
566 {
567  model = createModel();
568  sortingProxy = createSortingProxy(model);
569 
570  columnHeaderClicked(IDPlayers);
571  setupTableProperties(sortingProxy);
572 
573  connectTableModelProxySlots();
574  sortingProxy->setAdditionalSortColumns(gConfig.doomseeker.additionalSortColumns());
575 }
576 
577 void ServerListHandler::redraw()
578 {
579  model->redrawAll();
580 }
581 
582 void ServerListHandler::refreshAll()
583 {
584  for (int i = 0; i < model->rowCount(); ++i)
585  {
586  gRefresher->registerServer(model->serverFromList(i).data());
587  }
588 }
589 
590 void ServerListHandler::refreshSelected()
591 {
592  QItemSelectionModel* selectionModel = table->selectionModel();
593  QModelIndexList indexList = selectionModel->selectedRows();
594 
595  for(int i = 0; i < indexList.count(); ++i)
596  {
597  QModelIndex realIndex = sortingProxy->mapToSource(indexList[i]);
598  gRefresher->registerServer(model->serverFromList(realIndex).data());
599  }
600 }
601 
602 void ServerListHandler::removeAdditionalSortingForColumn(const QModelIndex &modelIndex)
603 {
604  sortingProxy->removeAdditionalColumnSorting(modelIndex.column());
605 }
606 
607 void ServerListHandler::saveAdditionalSortingConfig()
608 {
609  gConfig.doomseeker.setAdditionalSortColumns(sortingProxy->additionalSortColumns());
610 }
611 
612 void ServerListHandler::saveColumnsWidthsSettings()
613 {
614  gConfig.doomseeker.serverListColumnState = table->horizontalHeader()->saveState().toBase64();
615  gConfig.doomseeker.serverListSortIndex = sortIndex;
616  gConfig.doomseeker.serverListSortDirection = sortOrder;
617 }
618 
619 QList<ServerPtr> ServerListHandler::selectedServers()
620 {
621  QSortFilterProxyModel* pModel = static_cast<QSortFilterProxyModel*>(table->model());
622  QItemSelectionModel* selModel = table->selectionModel();
623  QModelIndexList indexList = selModel->selectedRows();
624 
625  QList<ServerPtr> servers;
626  for(int i = 0; i < indexList.count(); ++i)
627  {
628  QModelIndex realIndex = pModel->mapToSource(indexList[i]);
629  ServerPtr server = model->serverFromList(realIndex);
630  servers.append(server);
631  }
632  return servers;
633 }
634 
635 void ServerListHandler::serverBegunRefreshing(const ServerPtr &server)
636 {
637  model->setRefreshing(server);
638 }
639 
640 ServerPtr ServerListHandler::serverFromIndex(const QModelIndex &index)
641 {
642  QSortFilterProxyModel* pModel = static_cast<QSortFilterProxyModel*>(table->model());
643  QModelIndex indexReal = pModel->mapToSource(index);
644  return model->serverFromList(indexReal);
645 }
646 
647 void ServerListHandler::serverUpdated(const ServerPtr &server, int response)
648 {
649  int rowIndex = model->findServerOnTheList(server.data());
650  if (rowIndex >= 0)
651  {
652  rowIndex = model->updateServer(rowIndex, server, response);
653  }
654  else
655  {
656  rowIndex = model->addServer(server, response);
657  }
658 
659  needsCleaning = true;
660  emit serverInfoUpdated(server);
661 }
662 
664 {
665  const bool FORCE = true;
666  updateCountryFlags(!FORCE);
667 }
668 
669 void ServerListHandler::setGroupServersWithPlayersAtTop(bool b)
670 {
671  sortingProxy->setGroupServersWithPlayersAtTop(b);
672 }
673 
674 void ServerListHandler::setupTableColumnWidths()
675 {
676  QString &headerState = gConfig.doomseeker.serverListColumnState;
677  if(headerState.isEmpty())
678  {
679  for (int i = 0; i < NUM_SERVERLIST_COLUMNS; ++i)
680  {
681  ServerListColumn* columns = ServerListColumns::columns;
682  table->setColumnWidth(i, columns[i].width);
683  table->setColumnHidden(i, columns[i].bHidden);
684  if(!columns[i].bResizable)
685  {
686  table->horizontalHeader()->setResizeMode(i, QHeaderView::Fixed);
687  }
688  }
689  }
690  else
691  table->horizontalHeader()->restoreState(QByteArray::fromBase64(headerState.toAscii()));
692 
693  table->horizontalHeader()->setMovable(true);
694 
695  if(gConfig.doomseeker.serverListSortIndex >= 0)
696  {
697  sortIndex = gConfig.doomseeker.serverListSortIndex;
698  sortOrder = static_cast<Qt::SortOrder> (gConfig.doomseeker.serverListSortDirection);
699  }
700 }
701 
702 void ServerListHandler::setupTableProperties(QSortFilterProxyModel* tableModel)
703 {
704  table->setModel(tableModel);
705  table->setIconSize(QSize(26, 15));
706  // We don't really need a vertical header so lets remove it.
707  table->verticalHeader()->hide();
708  // Some flags that can't be set from the Designer.
709  table->horizontalHeader()->setSortIndicatorShown(true);
710  table->horizontalHeader()->setHighlightSections(false);
711 
712  table->setMouseTracking(true);
713 
714  setupTableColumnWidths();
715 }
716 
717 void ServerListHandler::sortAdditionally(const QModelIndex &modelIndex, Qt::SortOrder order)
718 {
719  ServerListProxyModel* model = static_cast<ServerListProxyModel*>(table->model());
720  model->addAdditionalColumnSorting(modelIndex.column(), order);
721 }
722 
723 Qt::SortOrder ServerListHandler::swapCurrentSortOrder()
724 {
725  if (sortOrder == Qt::AscendingOrder)
726  {
727  return Qt::DescendingOrder;
728  }
729  else
730  {
731  return Qt::AscendingOrder;
732  }
733 }
734 
735 void ServerListHandler::tableMiddleClicked(const QModelIndex& index, const QPoint& cursorPosition)
736 {
737  refreshSelected();
738 }
739 
740 void ServerListHandler::tableRightClicked(const QModelIndex& index, const QPoint& cursorPosition)
741 {
742  ServerPtr server = serverFromIndex(index);
743 
744  ServerListProxyModel* pModel = static_cast<ServerListProxyModel*>(table->model());
745  ServerListContextMenu *contextMenu = new ServerListContextMenu(server,
746  pModel->filterInfo(), index, this);
747  this->connect(contextMenu, SIGNAL(aboutToHide()), SLOT(contextMenuAboutToHide()));
748  this->connect(contextMenu, SIGNAL(triggered(QAction*)), SLOT(contextMenuTriggered(QAction*)));
749 
750  QPoint displayPoint = table->viewport()->mapToGlobal(cursorPosition);
751  contextMenu->popup(displayPoint);
752 }
753 
754 void ServerListHandler::updateCountryFlags()
755 {
756  const bool FORCE = true;
757  updateCountryFlags(FORCE);
758 }
759 
760 void ServerListHandler::updateCountryFlags(bool force)
761 {
762  for (int i = 0; i < model->rowCount(); ++i)
763  {
764  model->updateFlag(i, force);
765  }
766 }
767 
768 void ServerListHandler::updateHeaderTitles()
769 {
770  const QList<ColumnSort> &sortings = sortingProxy->additionalSortColumns();
771  QStringList labels;
772  ServerListColumns::generateColumnHeaderLabels(labels);
773  for (int i = 0; i < ServerListColumnId::NUM_SERVERLIST_COLUMNS; ++i)
774  {
775  // Clear header icons.
776  model->setHeaderData(i, Qt::Horizontal, QIcon(), Qt::DecorationRole);
777  }
778  for (int i = 0; i < sortings.size(); ++i)
779  {
780  const ColumnSort &sort = sortings[i];
781  labels[sort.columnId()] = QString("[%1] %2").arg(i + 1).arg(labels[sort.columnId()]);
782  QIcon icon = sort.order() == Qt::AscendingOrder ?
783  QIcon(":/icons/ascending.png") :
784  QIcon(":/icons/descending.png");
785  model->setHeaderData(sort.columnId(), Qt::Horizontal, icon, Qt::DecorationRole);
786  }
787  model->setHorizontalHeaderLabels(labels);
788 }
789 
790 void ServerListHandler::updateSearch(const QString& search)
791 {
792  QRegExp pattern(QString("*") + search + "*", Qt::CaseInsensitive, QRegExp::Wildcard);
793  sortingProxy->setFilterRegExp(pattern);
794 }
Structure describing server filter.
void setCountryFlagsIfNotPresent()
Sets country flags for servers that don't have flags present yet.
Definition: serverlist.cpp:663
Performs a case-insensitive (OS independent) file searches.
Definition: pathfinder.h:81
PWAD hosted on a server.
Message object used to pass messages throughout the Doomseeker's system.
Definition: message.h:62
A convenience wrapper class for GameExeFactory.
void findMissingWADs(const ServerPtr &)
virtual QString generalInfoHTML()
General info about server, like server name, version, email, etc.
void displayServerJoinCommandLine(const ServerPtr &)
virtual QString gameInfoTableHTML()
General info about current game (fraglimit, team scores, etc.)
void columnHeaderClicked(int)
Handles column sorting.
Definition: serverlist.cpp:132
void lookupHosts()
Looks up hosts for all available servers.
Definition: serverlist.cpp:506
This is returned when something was copied to clipboard.
int updateServer(int row, ServerPtr server, int response)
int addServer(ServerPtr server, int response)
int findServerOnTheList(const Server *server)
Finds index of the row where server is contained.
bool isOptional() const
Is this WAD required to join the server?
virtual QString playerTableHTML()
Player table that is created when cursor hovers over players column.
Wrapper for PathFinder that specializes in findings WADs.
Definition: wadpathfinder.h:54
void setFilterInfo(const ServerListFilterInfo &filterInfo)
Sets new filter info and immediately calls invalidate()
void serverInfoUpdated(const ServerPtr &)
Emitted every time when a server info is updated through serverUpdated()
const QString & name() const
File name of the WAD.