23 #include "serverlist.h"
25 #include "configuration/doomseekerconfig.h"
26 #include "gui/mainwindow.h"
27 #include "gui/models/serverlistcolumn.h"
28 #include "gui/models/serverlistmodel.h"
29 #include "gui/models/serverlistproxymodel.h"
30 #include "gui/remoteconsole.h"
31 #include "gui/widgets/serverlistcontextmenu.h"
32 #include "gui/widgets/serverlistview.h"
33 #include "refresher/refresher.h"
34 #include "serverapi/server.h"
35 #include "serverapi/tooltips/servertooltip.h"
36 #include "urlopener.h"
37 #include <QHeaderView>
38 #include <QMessageBox>
41 using namespace ServerListColumnId;
44 : mainWindow(pMainWindow), model(nullptr), needsCleaning(false),
45 proxyModel(nullptr), sortOrder(Qt::AscendingOrder),
46 sortIndex(-1), table(serverTable)
52 ServerList::~ServerList()
54 saveColumnsWidthsSettings();
59 gConfig.serverFilter.info = filterInfo;
64 bool ServerList::areColumnsWidthsSettingsChanged()
66 for (
int i = 0; i < NUM_SERVERLIST_COLUMNS; ++i)
68 if (ServerListColumns::columns[i].width != table->columnWidth(i))
75 void ServerList::cleanUp()
81 void ServerList::cleanUpRightNow()
83 if (mainWindow->isEffectivelyActiveWindow())
87 void ServerList::cleanUpForce()
89 if (table ==
nullptr || table->model() ==
nullptr)
96 pModel->sortServers(sortIndex, sortOrder);
100 needsCleaning =
false;
103 void ServerList::clearAdditionalSorting()
105 proxyModel->clearAdditionalSorting();
108 void ServerList::columnHeaderClicked(
int index)
110 if (isSortingByColumn(index))
111 sortOrder = swappedCurrentSortOrder();
113 sortOrder = getColumnDefaultSortOrder(index);
118 QHeaderView *header = table->horizontalHeader();
119 header->setSortIndicator(sortIndex, sortOrder);
122 void ServerList::connectTableModelProxySlots()
124 QHeaderView *header = table->horizontalHeader();
125 this->connect(header, SIGNAL(sectionClicked(
int)), SLOT(columnHeaderClicked(
int)));
127 this->connect(table->selectionModel(),
128 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
129 SLOT(itemSelected(QItemSelection)));
130 this->connect(table, SIGNAL(middleMouseClicked(QModelIndex,QPoint)),
131 SLOT(tableMiddleClicked()));
132 this->connect(table, SIGNAL(rightMouseClicked(QModelIndex,QPoint)),
133 SLOT(tableRightClicked(QModelIndex,QPoint)));
134 this->connect(table, SIGNAL(entered(QModelIndex)), SLOT(mouseEntered(QModelIndex)));
135 this->connect(table, SIGNAL(leftMouseDoubleClicked(QModelIndex,QPoint)),
136 SLOT(doubleClicked(QModelIndex)));
139 void ServerList::contextMenuAboutToHide()
141 sender()->deleteLater();
144 void ServerList::contextMenuTriggered(QAction *action)
147 ServerPtr server = contextMenu->server();
152 emit serverFilterModified(contextMenu->serverFilter());
155 switch (contextMenuResult)
161 case ServerListContextMenu::FindMissingWADs:
165 case ServerListContextMenu::Join:
166 emit serverDoubleClicked(server);
169 case ServerListContextMenu::OpenRemoteConsole:
173 case ServerListContextMenu::OpenURL:
177 UrlOpener::instance()->open(server->webSite());
184 case ServerListContextMenu::Refresh:
188 case ServerListContextMenu::ShowJoinCommandLine:
192 case ServerListContextMenu::SortAdditionallyAscending:
193 sortAdditionally(contextMenu->modelIndex(), Qt::AscendingOrder);
196 case ServerListContextMenu::SortAdditionallyDescending:
197 sortAdditionally(contextMenu->modelIndex(), Qt::DescendingOrder);
200 case ServerListContextMenu::RemoveAdditionalSortingForColumn:
201 removeAdditionalSortingForColumn(contextMenu->modelIndex());
204 case ServerListContextMenu::ClearAdditionalSorting:
205 clearAdditionalSorting();
208 case ServerListContextMenu::TogglePinServers:
209 for (
const ServerPtr &server : contextMenu->servers())
211 model->
redraw(server.data());
216 QMessageBox::warning(mainWindow, tr(
"Doomseeker - context menu warning"),
217 tr(
"Unhandled behavior in ServerList::contextMenuTriggered()"));
225 serverListModel->prepareHeaders();
226 return serverListModel;
232 this->connect(proxy, SIGNAL(additionalSortColumnsChanged()),
233 SLOT(updateHeaderTitles()));
234 this->connect(proxy, SIGNAL(additionalSortColumnsChanged()),
235 SLOT(saveAdditionalSortingConfig()));
236 proxy->setSourceModel(serverListModel);
237 proxy->setSortRole(ServerListModel::SLDT_SORT);
238 proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
239 proxy->setFilterKeyColumn(IDServerName);
244 void ServerList::doubleClicked(
const QModelIndex &index)
246 emit serverDoubleClicked(serverFromIndex(index));
249 Qt::SortOrder ServerList::getColumnDefaultSortOrder(
int columnId)
252 return ServerListColumns::columns[columnId].defaultSortOrder;
255 bool ServerList::hasAtLeastOneServer()
const
257 return model->rowCount() > 0;
260 void ServerList::initCleanerTimer()
262 cleanerTimer.setInterval(200);
263 cleanerTimer.start();
264 connect(&cleanerTimer, SIGNAL(timeout()),
this, SLOT (cleanUp()));
267 bool ServerList::isAnyColumnSortedAdditionally()
const
269 return proxyModel->isAnyColumnSortedAdditionally();
272 bool ServerList::isSortingAdditionallyByColumn(
int column)
const
274 return proxyModel->isSortingAdditionallyByColumn(column);
277 bool ServerList::isSortingByColumn(
int columnIndex)
279 return sortIndex == columnIndex;
282 void ServerList::itemSelected(
const QItemSelection &selection)
284 auto pModel =
static_cast<QSortFilterProxyModel *
>(table->model());
285 QModelIndexList indexList = selection.indexes();
287 QList<ServerPtr> servers;
288 for (
int i = 0; i < indexList.count(); ++i)
290 QModelIndex realIndex = pModel->mapToSource(indexList[i]);
291 ServerPtr server = model->serverFromList(realIndex);
292 servers.append(server);
294 emit serversSelected(servers);
299 for (
int i = 0; i < model->rowCount(); ++i)
301 ServerPtr server = model->serverFromList(i);
302 server->lookupHost();
306 void ServerList::mouseEntered(
const QModelIndex &index)
308 auto pModel =
static_cast<QSortFilterProxyModel *
>(table->model());
309 QModelIndex realIndex = pModel->mapToSource(index);
310 ServerPtr server = model->serverFromList(realIndex);
316 switch (index.column())
319 tooltip = ServerTooltip::createPortToolTip(server);
323 tooltip = server->hostName(
true);
327 tooltip = ServerTooltip::createPlayersToolTip(server);
331 tooltip = ServerTooltip::createServerNameToolTip(server);
335 tooltip = ServerTooltip::createIwadToolTip(server);
339 tooltip = ServerTooltip::createPwadsToolTip(server);
347 QToolTip::showText(QCursor::pos(), tooltip,
nullptr);
350 void ServerList::prepareServerTable()
352 model = createModel();
353 proxyModel = createSortingProxy(model);
355 columnHeaderClicked(IDPlayers);
356 table->setModel(proxyModel);
357 table->setupTableProperties();
359 if (gConfig.doomseeker.serverListSortIndex >= 0)
361 sortIndex = gConfig.doomseeker.serverListSortIndex;
362 sortOrder =
static_cast<Qt::SortOrder
>(gConfig.doomseeker.serverListSortDirection);
365 connectTableModelProxySlots();
366 proxyModel->setAdditionalSortColumns(gConfig.doomseeker.additionalSortColumns());
369 void ServerList::redraw()
374 void ServerList::refreshSelected()
376 for (
const ServerPtr &server : selectedServers())
378 gRefresher->registerServer(server.data());
382 void ServerList::registerServer(ServerPtr server)
385 if (serverOnList !=
nullptr)
387 serverOnList->setCustom(server->isCustom() || serverOnList->isCustom());
388 model->
redraw(serverOnList.data());
391 this->connect(server.data(), SIGNAL(updated(ServerPtr,
int)),
392 SLOT(onServerUpdated(ServerPtr)));
393 this->connect(server.data(), SIGNAL(begunRefreshing(ServerPtr)),
394 SLOT(onServerBegunRefreshing(ServerPtr)));
396 emit serverRegistered(server);
399 void ServerList::removeServer(
const ServerPtr &server)
401 server->disconnect(
this);
402 model->removeServer(server);
403 emit serverDeregistered(server);
406 void ServerList::removeCustomServers()
408 for (ServerPtr server : model->customServers())
410 removeServer(server);
414 void ServerList::removeNonSpecialServers()
416 for (ServerPtr server : model->nonSpecialServers())
418 removeServer(server);
422 void ServerList::removeAdditionalSortingForColumn(
const QModelIndex &modelIndex)
424 proxyModel->removeAdditionalColumnSorting(modelIndex.column());
427 void ServerList::saveAdditionalSortingConfig()
429 gConfig.doomseeker.setAdditionalSortColumns(proxyModel->additionalSortColumns());
432 void ServerList::saveColumnsWidthsSettings()
434 gConfig.doomseeker.serverListColumnState = table->horizontalHeader()->saveState().toBase64();
435 gConfig.doomseeker.serverListSortIndex = sortIndex;
436 gConfig.doomseeker.serverListSortDirection = sortOrder;
439 QList<ServerPtr> ServerList::selectedServers()
const
441 QModelIndexList indexList = table->selectionModel()->selectedRows();
443 QList<ServerPtr> servers;
444 for (
int i = 0; i < indexList.count(); ++i)
446 QModelIndex realIndex = proxyModel->mapToSource(indexList[i]);
447 ServerPtr server = model->serverFromList(realIndex);
448 servers.append(server);
453 void ServerList::onServerBegunRefreshing(
const ServerPtr &server)
455 model->setRefreshing(server);
458 QList<ServerPtr> ServerList::servers()
const
460 return model->servers();
463 ServerPtr ServerList::serverFromIndex(
const QModelIndex &index)
465 auto pModel =
static_cast<QSortFilterProxyModel *
>(table->model());
466 QModelIndex indexReal = pModel->mapToSource(index);
467 return model->serverFromList(indexReal);
470 QList<ServerPtr> ServerList::serversForPlugin(
const EnginePlugin *plugin)
const
472 return model->serversForPlugin(plugin);
475 void ServerList::onServerUpdated(
const ServerPtr &server)
483 needsCleaning =
true;
489 const bool FORCE =
true;
490 updateCountryFlags(!FORCE);
493 void ServerList::setGroupServersWithPlayersAtTop(
bool b)
495 proxyModel->setGroupServersWithPlayersAtTop(b);
498 void ServerList::sortAdditionally(
const QModelIndex &modelIndex, Qt::SortOrder order)
501 model->addAdditionalColumnSorting(modelIndex.column(), order);
504 Qt::SortOrder ServerList::swappedCurrentSortOrder()
506 return sortOrder == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
509 void ServerList::tableMiddleClicked()
514 void ServerList::tableRightClicked(
const QModelIndex &index,
const QPoint &cursorPosition)
516 ServerPtr server = serverFromIndex(index);
519 proxyModel->filterInfo(), index, selectedServers(),
this);
520 this->connect(contextMenu, SIGNAL(aboutToHide()), SLOT(contextMenuAboutToHide()));
521 this->connect(contextMenu, SIGNAL(triggered(QAction*)), SLOT(contextMenuTriggered(QAction*)));
523 QPoint displayPoint = table->viewport()->mapToGlobal(cursorPosition);
524 contextMenu->popup(displayPoint);
527 void ServerList::updateCountryFlags()
529 const bool FORCE =
true;
530 updateCountryFlags(FORCE);
533 void ServerList::updateCountryFlags(
bool force)
535 for (
int i = 0; i < model->rowCount(); ++i)
539 void ServerList::updateHeaderTitles()
541 const QList<ColumnSort> &sortings = proxyModel->additionalSortColumns();
542 for (
int i = 0; i < ServerListColumnId::NUM_SERVERLIST_COLUMNS; ++i)
545 model->setHeaderData(i, Qt::Horizontal, QIcon(), Qt::DecorationRole);
547 QStringList labels = ServerListColumns::generateColumnHeaderLabels();
548 for (
int i = 0; i < sortings.size(); ++i)
551 labels[sort.columnId()] = QString(
"[%1] %2").arg(i + 1).arg(labels[sort.columnId()]);
552 QIcon icon = sort.order() == Qt::AscendingOrder ?
553 QIcon(
":/icons/ascending.png") :
554 QIcon(
":/icons/descending.png");
555 model->setHeaderData(sort.columnId(), Qt::Horizontal, icon, Qt::DecorationRole);
557 model->setHorizontalHeaderLabels(labels);