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);
 
  560 void ServerList::updateSearch(
const QString &search)
 
  562         QRegExp pattern(QString(
"*") + search + 
"*", Qt::CaseInsensitive, QRegExp::Wildcard);
 
  563         proxyModel->setFilterRegExp(pattern);