23 #include "configuration/doomseekerconfig.h" 24 #include "configuration/queryspeed.h" 25 #include "gui/configuration/irc/ircconfigurationdialog.h" 26 #include "gui/configuration/doomseekerconfigurationdialog.h" 27 #include "gui/helpers/playersdiagram.h" 28 #include "gui/irc/ircdock.h" 29 #include "gui/irc/ircsounds.h" 30 #include "gui/models/serverlistmodel.h" 31 #include "gui/widgets/serversstatuswidget.h" 32 #include "gui/aboutdialog.h" 33 #include "gui/copytextdlg.h" 34 #include "gui/createserverdialog.h" 35 #include "gui/demomanager.h" 36 #include "gui/dockBuddiesList.h" 37 #include "gui/helpers/taskbarbutton.h" 38 #include "gui/helpers/taskbarprogress.h" 39 #include "gui/freedoomdialog.h" 40 #include "gui/ip2cupdatebox.h" 41 #include "gui/logdock.h" 42 #include "gui/mainwindow.h" 43 #include "gui/programargshelpdialog.h" 44 #include "gui/serverdetailsdock.h" 45 #include "gui/serverfilterdock.h" 46 #include "gui/serverlist.h" 47 #include "gui/wadseekerinterface.h" 48 #include "gui/wadseekershow.h" 49 #include "ip2c/ip2cloader.h" 50 #include "irc/configuration/chatnetworkscfg.h" 51 #include "irc/configuration/ircconfig.h" 52 #include "pathfinder/pathfinder.h" 53 #include "plugins/engineplugin.h" 54 #include "plugins/pluginloader.h" 55 #include "refresher/refresher.h" 56 #include "serverapi/broadcast.h" 57 #include "serverapi/broadcastmanager.h" 58 #include "serverapi/gameclientrunner.h" 59 #include "serverapi/mastermanager.h" 60 #include "serverapi/message.h" 61 #include "serverapi/server.h" 62 #include "serverapi/serverlistcounttracker.h" 63 #include "updater/autoupdater.h" 64 #include "updater/updatechannel.h" 65 #include "updater/updateinstaller.h" 66 #include "updater/updatepackage.h" 67 #include "application.h" 68 #include "apprunner.h" 69 #include "commandline.h" 70 #include "connectionhandler.h" 71 #include "customservers.h" 72 #include "datapaths.h" 73 #include "doomseekerfilepaths.h" 74 #include "fileutils.h" 76 #include "joincommandlinebuilder.h" 77 #include "pathfinder/wadpathfinder.h" 80 #include "strings.hpp" 81 #include "ui_mainwindow.h" 84 #include <QApplication> 85 #include <QDesktopServices> 86 #include <QDesktopWidget> 87 #include <QDockWidget> 89 #include <QHeaderView> 92 #include <QMessageBox> 94 #include <QProgressBar> 96 #include <QSizePolicy> 97 #include <QMessageBox> 99 const QString MainWindow::HELP_SITE_URL =
"https://doomseeker.drdteam.org/help";
108 class QueryMenuAction :
public QAction
114 this->pPlugin = plugin;
118 connect(
this, SIGNAL( toggled(
bool) ), plugin->data()->masterClient,
119 SLOT( setEnabled(
bool) ) );
120 connect(
this, SIGNAL( toggled(
bool) ), statusWidget, SLOT( setMasterEnabledStatus(
bool) ) );
133 DClass<MainWindow> :
public Ui::MainWindowWnd
136 PrivData() : bTotalRefreshInProcess(false), buddiesList(NULL),
137 bWasMaximized(false), bWantToQuit(false), logDock(NULL),
138 masterManager(NULL), trayIcon(NULL), trayIconMenu(NULL)
142 QApplication* application;
143 QTimer autoRefreshTimer;
146 QWidget* autoUpdaterStatusBarWidget;
147 QPushButton* autoUpdaterAbortButton;
148 QLabel* autoUpdaterLabel;
149 QProgressBar* autoUpdaterFileProgressBar;
150 QProgressBar* autoUpdaterOverallProgressBar;
156 bool bTotalRefreshInProcess;
174 QProgressBar* ip2cUpdateProgressBar;
178 QPointer<FreedoomDialog> freedoomDialog;
183 QHash<const EnginePlugin*, QueryMenuAction*> queryMenuPorts;
184 QHash<const EnginePlugin*, ServersStatusWidget*> serversStatusesWidgets;
185 QAction* toolBarGetServers;
186 QSystemTrayIcon* trayIcon;
190 int updaterInstallerErrorCode;
193 QDockWidget *mainDock;
203 d->autoUpdater = NULL;
205 d->connectionHandler = NULL;
207 d->updaterInstallerErrorCode = 0;
209 d->application = application;
211 this->setAttribute(Qt::WA_DeleteOnClose,
true);
215 d->taskbarProgress = d->taskbarButton->progress();
219 initAutoUpdaterWidgets();
221 d->updatesConfirmationWidget->hide();
222 d->updatesDownloadedWidget->hide();
225 #ifndef WITH_AUTOUPDATES 226 d->menuActionCheckForUpdates->setVisible(
false);
229 if (gPlugins->numPlugins() == 0)
231 QString message = MainWindow::tr(
233 "Doomseeker failed to detect any plugins.\n" 234 "While the core application will still work its functionality is going to be limited.\n" 235 "One of the proper locations for plugin modules is the \"engines/\" directory.\n" 238 QMessageBox::critical(NULL, MainWindow::tr(
"Doomseeker - plugin load failure"), message);
245 d->menuView->addAction(d->buddiesList->toggleViewAction());
246 d->buddiesList->toggleViewAction()->setText(MainWindow::tr(
"&Buddies"));
247 d->buddiesList->toggleViewAction()->setShortcut(MainWindow::tr(
"CTRL+B"));
249 connect(d->buddiesList, SIGNAL(joinServer(ServerPtr)),
this, SLOT(runGame(ServerPtr)));
250 d->buddiesList->hide();
251 this->addDockWidget(Qt::LeftDockWidgetArea, d->buddiesList);
254 initServerFilterDock();
256 splitDockWidget(d->mainDock, d->serverFilterDock, Qt::Horizontal);
259 d->serverList =
new ServerList(d->tableServers,
this);
263 d->serverList->connect(d->broadcastManager,
264 SIGNAL(newServerDetected(ServerPtr,
int)), SLOT(registerServer(ServerPtr)));
265 d->serverList->connect(d->broadcastManager,
266 SIGNAL(serverLost(ServerPtr)), SLOT(removeServer(ServerPtr)));
268 initServerDetailsDock();
269 tabifyDockWidget(d->ircDock, d->detailsDock);
272 d->menuActionRecordDemo->setChecked(gConfig.doomseeker.bRecordDemo);
276 d->buddiesList->scan(d->masterManager);
277 connect(d->masterManager, SIGNAL( masterMessage(
MasterClient*,
const QString&,
const QString&,
bool) ),
278 this, SLOT( masterManagerMessages(
MasterClient*,
const QString&,
const QString&,
bool) ) );
283 fillQueryMenu(d->masterManager);
286 QList<ServerPtr> customServers = d->masterManager->customServs()->readConfig();
287 foreach (ServerPtr server, customServers)
289 d->serverList->registerServer(server);
292 setWindowIcon(Application::icon());
295 initAutoRefreshTimer();
296 connect(&d->autoRefreshTimer, SIGNAL( timeout() ),
this, SLOT( autoRefreshTimer_timeout() ));
307 d->menuActionUpdateIP2C->setEnabled(
false);
309 connectIP2CLoader(d->ip2cLoader);
310 d->ip2cLoader->load();
312 restoreState(QByteArray::fromBase64(gConfig.doomseeker.mainWindowState.toUtf8()));
313 restoreGeometry(gConfig.doomseeker.mainWindowGeometry);
317 QTimer::singleShot(1,
this, SLOT(postInitAppStartup()));
320 MainWindow::~MainWindow()
323 gConfig.doomseeker.mainWindowGeometry = saveGeometry();
324 gConfig.doomseeker.mainWindowState = saveState().toBase64();
326 if (d->updateChannelOnUpdateStart != NULL)
328 delete d->updateChannelOnUpdateStart;
330 if (d->autoUpdater != NULL)
332 d->autoUpdater->disconnect();
333 delete d->autoUpdater;
335 if(d->connectionHandler)
336 delete d->connectionHandler;
338 QList<QAction*> menuQueryActions = d->menuQuery->actions();
339 QList<QAction*>::iterator it;
340 foreach (QAction *action, menuQueryActions)
342 QString pluginName = action->text();
344 if (!pluginName.isEmpty())
346 IniSection pluginConfig = gConfig.iniSectionForPlugin(pluginName);
347 pluginConfig[
"Query"] = action->isChecked();
351 if (d->trayIcon != NULL)
353 d->trayIcon->setVisible(
false);
358 if (d->trayIconMenu != NULL)
360 delete d->trayIconMenu;
361 d->trayIconMenu = NULL;
364 delete d->serverList;
366 if(d->masterManager != NULL)
368 delete d->masterManager;
371 if (d->ip2cLoader != NULL)
373 delete d->ip2cLoader;
377 void MainWindow::abortAutoUpdater()
379 if (d->autoUpdater != NULL)
381 d->autoUpdater->abort();
385 void MainWindow::autoRefreshTimer_timeout()
387 if (gConfig.doomseeker.bQueryAutoRefreshDontIfActive && !isMinimized())
389 if (QApplication::activeWindow() != 0)
398 void MainWindow::blockRefreshButtons()
400 d->toolBarGetServers->setEnabled(
false);
405 return d->buddiesList;
408 void MainWindow::changeEvent(QEvent* event)
410 if (event->type() == QEvent::ActivationChange && isActiveWindow() && !isMinimized() && !isHidden())
412 d->serverList->cleanUp();
415 QMainWindow::changeEvent(event);
418 void MainWindow::checkForUpdates(
bool bUserTriggered)
420 if (d->autoUpdater != NULL)
422 if (d->autoUpdater->isRunning())
424 QMessageBox::warning(
this, tr(
"Doomseeker - Auto Update"),
425 tr(
"Update is already in progress."));
430 delete d->autoUpdater;
431 d->autoUpdater = NULL;
434 gLog << tr(
"Removing old update packages from local temporary space.");
435 QStringList removeFilter(QString(
"%1*").arg(DataPaths::UPDATE_PACKAGE_FILENAME_PREFIX));
439 showAndLogStatusMessage(tr(
"Checking for updates..."));
441 this->connect(d->autoUpdater, SIGNAL(statusMessage(QString)),
442 SLOT(showAndLogStatusMessage(QString)));
443 this->connect(d->autoUpdater, SIGNAL(finished()),
444 SLOT(onAutoUpdaterFinish()));
445 this->connect(d->autoUpdater, SIGNAL(downloadAndInstallConfirmationRequested()),
446 SLOT(onAutoUpdaterDownloadAndInstallConfirmationRequest()));
447 this->connect(d->autoUpdater, SIGNAL(overallProgress(
int,
int,
const QString&)),
448 SLOT(onAutoUpdaterOverallProgress(
int,
int,
const QString&)));
449 this->connect(d->autoUpdater, SIGNAL(packageDownloadProgress(qint64, qint64)),
450 SLOT(onAutoUpdaterFileProgress(qint64, qint64)));
452 QMap<QString, QList<QString> > ignoredPackagesRevisions;
455 foreach (
const QString& package, gConfig.autoUpdates.lastKnownUpdateRevisions.keys())
457 QString revision = gConfig.autoUpdates.lastKnownUpdateRevisions[package];
460 ignoredPackagesRevisions.insert(package, list);
463 d->autoUpdater->setIgnoreRevisions(ignoredPackagesRevisions);
466 d->autoUpdater->setChannel(channel);
467 *d->updateChannelOnUpdateStart = channel;
469 bool bRequireConfirmation =
true;
472 bRequireConfirmation = (gConfig.autoUpdates.updateMode
475 d->autoUpdater->setRequireDownloadAndInstallConfirmation(bRequireConfirmation);
476 d->autoUpdaterStatusBarWidget->show();
477 d->autoUpdater->start();
483 const bool bUserTriggered =
true;
484 checkForUpdates(!bUserTriggered);
489 const bool bUserTriggered =
true;
490 checkForUpdates(bUserTriggered);
493 void MainWindow::closeEvent(QCloseEvent* event)
498 if (d->trayIcon != NULL && gConfig.doomseeker.bCloseToTrayIcon && !d->bWantToQuit)
500 d->bWasMaximized = isMaximized();
510 void MainWindow::confirmUpdateInstallation()
512 assert(d->autoUpdater != NULL &&
"MainWindow::confirmUpdateInstallation()");
513 d->updatesConfirmationWidget->hide();
514 d->autoUpdater->confirmDownloadAndInstall();
517 void MainWindow::connectIP2CLoader(
IP2CLoader* loader)
519 this->connect(d->ip2cLoader, SIGNAL( finished() ), SLOT( ip2cJobsFinished()));
520 this->connect(d->ip2cLoader, SIGNAL( downloadProgress(qint64, qint64) ),
521 SLOT( ip2cDownloadProgress(qint64, qint64) ) );
524 void MainWindow::discardUpdates()
526 assert(d->autoUpdater != NULL &&
"MainWindow::confirmUpdateInstallation()");
527 d->updatesConfirmationWidget->hide();
530 const QList<UpdatePackage>& pkgList = d->autoUpdater->newUpdatePackages();
533 gConfig.autoUpdates.lastKnownUpdateRevisions.insert(pkg.
name, pkg.
revision);
535 d->autoUpdater->abort();
541 connect(gRefresher, SIGNAL( block() ),
this, SLOT( blockRefreshButtons() ) );
542 connect(gRefresher, SIGNAL( finishedQueryingMaster(
MasterClient*) ),
this, SLOT( finishedQueryingMaster(
MasterClient*) ) );
543 connect(gRefresher, SIGNAL( sleepingModeEnter() ),
this, SLOT( refreshThreadEndsWork() ) );
544 connect(gRefresher, SIGNAL( sleepingModeEnter() ), d->buddiesList, SLOT( scan() ) );
545 connect(gRefresher, SIGNAL( sleepingModeExit() ),
this, SLOT( refreshThreadBeginsWork() ) );
548 connect(d->menuActionAbout, SIGNAL( triggered() ),
this, SLOT( menuHelpAbout() ));
549 connect(d->menuActionAboutQt, SIGNAL( triggered() ), d->application, SLOT( aboutQt() ));
550 connect(d->menuActionBuddies, SIGNAL( triggered() ),
this, SLOT( menuBuddies() ));
551 connect(d->menuActionConfigure, SIGNAL( triggered() ),
this, SLOT( menuOptionsConfigure() ));
552 connect(d->menuActionCreateServer, SIGNAL( triggered() ),
this, SLOT( menuCreateServer() ));
553 connect(d->menuActionHelp, SIGNAL( triggered() ),
this, SLOT ( menuHelpHelp() ) );
554 connect(d->menuActionIRCOptions, SIGNAL( triggered() ),
this, SLOT( menuIRCOptions() ) );
555 connect(d->menuActionLog, SIGNAL( triggered() ),
this, SLOT( menuLog() ));
556 connect(d->menuActionManageDemos, SIGNAL( triggered() ),
this, SLOT( menuManageDemos() ) );
557 connect(d->menuActionRecordDemo, SIGNAL( triggered() ),
this, SLOT( menuRecordDemo() ) );
558 connect(d->menuActionUpdateIP2C, SIGNAL( triggered() ),
this, SLOT( menuUpdateIP2C() ) );
559 connect(d->menuActionQuit, SIGNAL( triggered() ),
this, SLOT( quitProgram() ));
560 connect(d->menuActionViewIRC, SIGNAL( triggered() ) ,
this, SLOT( menuViewIRC() ));
561 connect(d->menuActionWadseeker, SIGNAL( triggered() ),
this, SLOT( menuWadSeeker() ));
564 connect(d->serverFilterDock, SIGNAL(nonEmptyServerGroupingAtTopToggled(
bool)),
565 d->serverList, SLOT(setGroupServersWithPlayersAtTop(
bool)) );
568 connect(d->serverList, SIGNAL( serverDoubleClicked(ServerPtr) ),
this, SLOT( runGame(ServerPtr) ) );
569 connect(d->serverList, SIGNAL( displayServerJoinCommandLine(
const ServerPtr&) ),
this, SLOT( showServerJoinCommandLine(
const ServerPtr&) ) );
570 connect(d->serverList, SIGNAL( findMissingWADs(
const ServerPtr&) ),
this, SLOT( findMissingWADs(
const ServerPtr&) ) );
571 connect(d->serverList, SIGNAL( serverInfoUpdated(ServerPtr) ),
this, SLOT( serverAddedToList(ServerPtr) ) );
572 connect(d->buddiesList, SIGNAL(scanCompleted()), d->serverList, SLOT(redraw()));
579 for(
unsigned i = 0; i < gPlugins->numPlugins(); ++i)
582 if(!plugin->data()->hasMasterClient() && !plugin->data()->hasBroadcast())
587 if (plugin->data()->hasMasterClient())
589 MasterClient* pMasterClient = plugin->data()->masterClient;
590 pMasterClient->updateAddress();
591 masterManager->addMaster(pMasterClient);
594 if (plugin->data()->hasBroadcast())
596 d->broadcastManager->registerPlugin(plugin);
601 d->serversStatusesWidgets.insert(plugin, statusWidget);
603 this->connect(statusWidget, SIGNAL( clicked(
const EnginePlugin*) ) ,
604 SLOT( togglePluginQueryEnabled(
const EnginePlugin*) ) );
605 this->connect(statusWidget, SIGNAL(counterUpdated()),
606 SLOT(updateRefreshProgress()));
608 statusBar()->addPermanentWidget(statusWidget);
610 QString name = gPlugins->info(i)->data()->name;
611 QueryMenuAction* query =
new QueryMenuAction(plugin, statusWidget, d->menuQuery);
612 d->queryMenuPorts.insert(plugin, query);
614 d->menuQuery->addAction(query);
616 query->setCheckable(
true);
617 query->setIcon(plugin->icon());
618 query->setText(name);
620 IniSection pluginConfig = gConfig.iniSectionForPlugin(name);
624 bool enabled = pluginConfig[
"Query"];
625 setQueryPluginEnabled(plugin, enabled);
631 setQueryPluginEnabled(plugin,
true);
636 void MainWindow::findMissingWADs(
const ServerPtr &server)
639 QList<PWad> wads = server->wads();
640 wads << server->iwad();
641 PathFinder pathFinder = server->wadPathFinder();
643 QList<PWad> missingWads;
644 foreach(
const PWad &wad, wads)
646 if(!wadFinder.find(wad.
name()).isValid())
649 missingWads << optionalWad;
652 if (missingWads.isEmpty())
654 QMessageBox::information(
this, tr(
"All WADs found"), tr(
"All of the WADs used by this server are present."));
659 dialog.setAllowIgnore(
false);
660 if (dialog.exec() == QDialog::Accepted && dialog.decision() == MissingWadsDialog::Install)
662 if (!gWadseekerShow->checkWadseekerValidity(
this))
667 wadseeker->setCustomSite(server->webSite());
668 wadseeker->
setWads(dialog.filesToDownload());
669 wadseeker->setAttribute(Qt::WA_DeleteOnClose);
677 updateMasterAddresses();
678 gRefresher->setDelayBetweenResends(gConfig.doomseeker.querySpeed().delayBetweenSingleServerAttempts);
681 if (configDialog.wasAppearanceChanged())
683 updateDynamicAppearance();
688 if (configDialog.isRestartNeeded())
690 QString warningRestartNeeded = tr(
"Doomseeker needs to be restarted for some changes to be applied.");
691 d->importantMessagesWidget->addMessage(warningRestartNeeded);
695 if (lookupHostsChanged)
697 d->serverList->lookupHosts();
701 if (configDialog.customServersChanged())
703 d->serverList->removeCustomServers();
704 QList<ServerPtr> servers = d->masterManager->customServs()->readConfig();
705 foreach (ServerPtr server, servers)
707 d->serverList->registerServer(server);
709 refreshCustomServers();
713 void MainWindow::finishedQueryingMaster(
MasterClient* master)
720 for(
int i = 0;i < master->numServers();i++)
722 d->serverList->registerServer((*master)[i]);
726 void MainWindow::fixIconsDpi()
729 QIcon icon(
":/icons/exclamation_16.png");
730 d->lblExclamation1->setPixmap(icon.pixmap(16));
731 d->lblExclamation2->setPixmap(icon.pixmap(16));
734 void MainWindow::getServers()
737 if (!isAnythingToRefresh())
739 QString message = tr(
"Doomseeker is unable to proceed with the refresh" 740 " operation because the following problem has occured:\n\n");
742 if (gPlugins->numPlugins() == 0)
744 message += tr(
"Plugins are missing from the \"engines/\" directory.");
746 else if (!isAnyMasterEnabled())
748 message += tr(
"No master servers are enabled in the \"Query\" menu.");
752 message += tr(
"Unknown error occured.");
756 QMessageBox::warning(
this, tr(
"Doomseeker - refresh problem"), message);
760 d->bTotalRefreshInProcess =
true;
761 d->autoRefreshTimer.stop();
762 gLog << tr(
"Total refresh initialized!");
767 d->serverList->removeNonSpecialServers();
768 refreshServersOnList();
770 if (!isAnyMasterEnabled() && !d->serverList->hasAtLeastOneServer())
772 gLog << tr(
"Warning: No master servers were enabled for this refresh. " 773 "Check your Query menu or \"engines/\" directory.");
776 d->masterManager->clearServers();
777 for (
int i = 0; i < d->masterManager->numMasters(); ++i)
783 gRefresher->registerMaster(pMaster);
788 bool MainWindow::hasCustomServers()
const 790 CustomServers* customServers = d->masterManager->customServs();
791 return customServers->numServers() > 0;
796 const unsigned MIN_DELAY = 30;
797 const unsigned MAX_DELAY = 3600;
799 bool bEnabled = gConfig.doomseeker.bQueryAutoRefreshEnabled;
803 d->autoRefreshTimer.stop();
809 unsigned& delay = gConfig.doomseeker.queryAutoRefreshEverySeconds;
812 if (delay < MIN_DELAY)
816 else if (delay > MAX_DELAY)
821 unsigned delayMs = delay * 1000;
823 d->autoRefreshTimer.setSingleShot(
false);
824 d->autoRefreshTimer.start(delayMs);
828 void MainWindow::initAutoUpdaterWidgets()
830 static const int FILE_BAR_WIDTH = 50;
831 static const int OVERALL_BAR_WIDTH = 180;
833 d->autoUpdaterStatusBarWidget =
new QWidget(statusBar());
834 d->autoUpdaterStatusBarWidget->setLayout(
new QHBoxLayout(d->autoUpdaterStatusBarWidget));
835 d->autoUpdaterStatusBarWidget->layout()->setContentsMargins(QMargins(0, 0, 0, 0));
836 statusBar()->addPermanentWidget(d->autoUpdaterStatusBarWidget);
837 d->autoUpdaterStatusBarWidget->hide();
839 d->autoUpdaterLabel =
new QLabel(d->autoUpdaterStatusBarWidget);
840 d->autoUpdaterLabel->setText(tr(
"Auto Updater:"));
841 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterLabel);
843 d->autoUpdaterFileProgressBar = mkStdProgressBarForStatusBar();
844 d->autoUpdaterFileProgressBar->setFormat(
"%p%");
845 d->autoUpdaterFileProgressBar->setMaximumWidth(FILE_BAR_WIDTH);
846 d->autoUpdaterFileProgressBar->setMinimumWidth(FILE_BAR_WIDTH);
847 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterFileProgressBar);
849 d->autoUpdaterOverallProgressBar = mkStdProgressBarForStatusBar();
850 d->autoUpdaterOverallProgressBar->setMaximumWidth(OVERALL_BAR_WIDTH);
851 d->autoUpdaterOverallProgressBar->setMinimumWidth(OVERALL_BAR_WIDTH);
852 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterOverallProgressBar);
854 d->autoUpdaterAbortButton =
new QPushButton(statusBar());
855 d->autoUpdaterAbortButton->setToolTip(tr(
"Abort update."));
856 d->autoUpdaterAbortButton->setIcon(QIcon(
":/icons/x.png"));
857 this->connect(d->autoUpdaterAbortButton, SIGNAL(clicked()),
858 SLOT(abortAutoUpdater()));
859 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterAbortButton);
862 void MainWindow::initIP2CUpdater()
864 static const int PROGRESSBAR_WIDTH = 220;
866 d->ip2cUpdateProgressBar = mkStdProgressBarForStatusBar();
867 d->ip2cUpdateProgressBar->setFormat(tr(
"IP2C Update"));
868 d->ip2cUpdateProgressBar->hide();
869 d->ip2cUpdateProgressBar->setMaximumWidth(PROGRESSBAR_WIDTH);
870 d->ip2cUpdateProgressBar->setMinimumWidth(PROGRESSBAR_WIDTH);
871 statusBar()->addPermanentWidget(d->ip2cUpdateProgressBar);
874 void MainWindow::initIRCDock()
876 d->ircDock =
new IRCDock(
this);
877 d->menuView->addAction(d->ircDock->toggleViewAction());
878 d->ircDock->toggleViewAction()->setText(tr(
"&IRC"));
879 d->ircDock->toggleViewAction()->setShortcut(tr(
"CTRL+I"));
881 this->addDockWidget(Qt::BottomDockWidgetArea, d->ircDock);
885 this->d->ircDock->setVisible(
true);
886 this->d->ircDock->performNetworkAutojoins();
890 void MainWindow::initLogDock()
892 d->logDock =
new LogDock(
this);
893 d->menuView->addAction(d->logDock->toggleViewAction());
894 d->logDock->toggleViewAction()->setText(tr(
"&Log"));
895 d->logDock->toggleViewAction()->setShortcut(tr(
"CTRL+L"));
897 this->addDockWidget(Qt::BottomDockWidgetArea, d->logDock);
899 connect(&gLog, SIGNAL( newEntry(
const QString&) ), d->logDock, SLOT( appendLogEntry(
const QString&) ) );
902 d->logDock->appendLogEntry(gLog.content());
905 void MainWindow::initMainDock()
907 setDockNestingEnabled(
true);
911 d->mainDock =
new QDockWidget(tr(
"Servers"));
912 d->mainDock->setTitleBarWidget(
new QWidget(
this));
913 d->mainDock->setObjectName(
"ServerList");
914 d->mainDock->setFeatures(QDockWidget::NoDockWidgetFeatures);
915 d->mainDock->setWidget(centralWidget());
917 addDockWidget(Qt::RightDockWidgetArea, d->mainDock);
920 void MainWindow::initServerDetailsDock()
923 d->menuView->addAction(d->detailsDock->toggleViewAction());
924 d->detailsDock->toggleViewAction()->setText(tr(
"Server &Details"));
925 d->detailsDock->toggleViewAction()->setShortcut(tr(
"CTRL+D"));
926 d->detailsDock->hide();
927 addDockWidget(Qt::BottomDockWidgetArea, d->detailsDock);
929 d->detailsDock->connect(d->serverList, SIGNAL( serversSelected(QList<ServerPtr>&) ), SLOT( displaySelection(QList<ServerPtr> &) ));
932 void MainWindow::initServerFilterDock()
936 d->menuView->addAction(d->serverFilterDock->toggleViewAction());
937 d->serverFilterDock->hide();
938 this->addDockWidget(Qt::RightDockWidgetArea, d->serverFilterDock);
943 bool isEnabled = gConfig.doomseeker.bUseTrayIcon;
944 if (!isEnabled || !QSystemTrayIcon::isSystemTrayAvailable())
946 if (d->trayIcon != NULL)
952 if (d->trayIconMenu != NULL)
954 delete d->trayIconMenu;
955 d->trayIconMenu = NULL;
958 else if (d->trayIcon == NULL)
961 d->trayIconMenu =
new QMenu(
this);
962 trayAction = d->trayIconMenu->addAction(
"Exit");
963 connect(trayAction, SIGNAL( triggered() ),
this, SLOT( quitProgram() ) );
966 d->trayIcon =
new QSystemTrayIcon(
this);
967 connect(d->trayIcon, SIGNAL( activated(QSystemTrayIcon::ActivationReason) ),
this, SLOT( trayIcon_activated(QSystemTrayIcon::ActivationReason) ) );
969 updateTrayIconTooltipAndLogTotalRefresh();
971 d->trayIcon->setContextMenu(d->trayIconMenu);
972 d->trayIcon->setIcon(QIcon(
":/icon.png"));
973 d->trayIcon->setVisible(
true);
977 void MainWindow::ip2cDownloadProgress(qint64 current, qint64 max)
979 d->ip2cUpdateProgressBar->setMaximum(max);
980 d->ip2cUpdateProgressBar->setValue(current);
983 void MainWindow::ip2cJobsFinished()
985 d->menuActionUpdateIP2C->setEnabled(
true);
986 d->serverList->updateCountryFlags();
987 d->ip2cUpdateProgressBar->hide();
989 if (d->ip2cLoader != NULL)
991 delete d->ip2cLoader;
992 d->ip2cLoader = NULL;
996 void MainWindow::ip2cStartUpdate()
998 if (d->ip2cLoader != NULL)
1004 d->menuActionUpdateIP2C->setEnabled(
false);
1005 d->ip2cUpdateProgressBar->show();
1008 connectIP2CLoader(d->ip2cLoader);
1009 d->ip2cLoader->update();
1014 return hasCustomServers() || isAnyMasterEnabled();
1017 bool MainWindow::isAnyMasterEnabled()
const 1019 for (
int i = 0; i < d->masterManager->numMasters(); ++i)
1032 bool MainWindow::isEffectivelyActiveWindow()
const 1034 return this->isActiveWindow() || DoomseekerConfigurationDialog::isOpen();
1037 void MainWindow::masterManagerMessages(
MasterClient* pSender,
const QString& title,
const QString& content,
bool isError)
1039 QString message = tr(
"Master server for %1: %2").arg(pSender->
plugin()->data()->name).arg(content);
1043 message = tr(
"Error: %1").arg(message);
1044 statusBar()->showMessage(message);
1050 void MainWindow::masterManagerMessagesImportant(
MasterClient* pSender,
const Message& objMessage)
1052 QString strFullMessage = tr(
"%1: %2")
1053 .arg(pSender->
plugin()->data()->name)
1055 d->importantMessagesWidget->addMessage(strFullMessage, objMessage.
timestamp());
1058 void MainWindow::menuBuddies()
1060 d->buddiesList->setVisible(!d->buddiesList->isVisible());
1063 void MainWindow::menuCreateServer()
1067 dialog->setWindowIcon(this->windowIcon());
1071 void MainWindow::menuHelpAbout()
1074 d->autoRefreshTimer.stop();
1076 initAutoRefreshTimer();
1079 void MainWindow::menuHelpHelp()
1083 QMessageBox::critical(
this, tr(
"Help error"), tr(
"No help found."), QMessageBox::Ok, QMessageBox::Ok);
1087 bool bSuccess = QDesktopServices::openUrl(HELP_SITE_URL);
1091 QMessageBox::critical(
this, tr(
"Help error"), tr(
"Failed to open URL:\n%1").arg(HELP_SITE_URL), QMessageBox::Ok, QMessageBox::Ok);
1096 void MainWindow::menuIRCOptions()
1099 dialog.initOptionsList();
1102 if (d->ircDock != NULL)
1104 d->ircDock->applyAppearanceSettings();
1109 d->ircDock->sounds().loadFromConfig();
1113 void MainWindow::menuLog()
1115 d->logDock->setVisible(!d->logDock->isVisible());
1118 void MainWindow::menuManageDemos()
1121 dm.setWindowIcon(this->windowIcon());
1125 void MainWindow::menuOptionsConfigure()
1127 DoomseekerConfigurationDialog::openConfiguration(
this);
1130 void MainWindow::menuRecordDemo()
1132 gConfig.doomseeker.bRecordDemo = d->menuActionRecordDemo->isChecked();
1135 void MainWindow::menuUpdateIP2C()
1139 connect(&updateBox, SIGNAL( accepted() ),
this, SLOT( ip2cStartUpdate() ) );
1144 void MainWindow::menuViewIRC()
1146 d->ircDock->setVisible(!d->ircDock->isVisible());
1149 void MainWindow::menuWadSeeker()
1151 if (gWadseekerShow->checkWadseekerValidity(
this))
1154 wadseeker->setAttribute(Qt::WA_DeleteOnClose);
1159 QProgressBar* MainWindow::mkStdProgressBarForStatusBar()
1161 QProgressBar* pBar =
new QProgressBar(statusBar());
1162 pBar->setAlignment(Qt::AlignCenter);
1163 pBar->setTextVisible(
true);
1164 pBar->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1168 void MainWindow::notifyFirstRun()
1171 QMessageBox::information(NULL, tr(
"Welcome to Doomseeker"),
1172 tr(
"Before you start browsing for servers, please ensure that Doomseeker is properly configured."));
1173 d->menuActionConfigure->trigger();
1176 void MainWindow::onAutoUpdaterDownloadAndInstallConfirmationRequest()
1178 d->updatesConfirmationWidget->show();
1181 void MainWindow::onAutoUpdaterFileProgress(qint64 bytesReceived, qint64 bytesTotal)
1183 d->autoUpdaterFileProgressBar->setValue(bytesReceived);
1184 d->autoUpdaterFileProgressBar->setMaximum(bytesTotal);
1187 void MainWindow::onAutoUpdaterFinish()
1189 showAndLogStatusMessage(tr(
"Program update detection & download finished with status: [%1] %2")
1190 .arg((
int)d->autoUpdater->errorCode()).arg(d->autoUpdater->errorString()));
1191 gConfig.autoUpdates.bPerformUpdateOnNextRun =
false;
1192 if (d->autoUpdater->errorCode() == AutoUpdater::EC_Ok)
1195 if (channel == *d->updateChannelOnUpdateStart)
1197 if (!d->autoUpdater->downloadedPackagesFilenames().isEmpty())
1199 gLog << tr(
"Updates will be installed on next program start.");
1200 d->updatesDownloadedWidget->show();
1201 gConfig.autoUpdates.bPerformUpdateOnNextRun =
true;
1206 gLog << tr(
"Update channel was changed during update process. Discarding update.");
1209 gConfig.saveToFile();
1210 d->autoUpdaterStatusBarWidget->hide();
1211 d->updatesConfirmationWidget->hide();
1212 d->autoUpdater->deleteLater();
1213 d->autoUpdater = NULL;
1216 void MainWindow::onAutoUpdaterOverallProgress(
int current,
int total,
1219 d->autoUpdaterOverallProgressBar->setValue(current);
1220 d->autoUpdaterOverallProgressBar->setMaximum(total);
1221 d->autoUpdaterOverallProgressBar->setFormat(msg);
1224 void MainWindow::postInitAppStartup()
1227 d->serverFilterDock->setFilterInfo(gConfig.serverFilter.info);
1233 if (gPlugins->numPlugins() > 0)
1235 bool bGettingServers =
false;
1236 bool queryOnStartup = gConfig.doomseeker.bQueryOnStartup;
1243 if (isAnyMasterEnabled())
1245 bGettingServers =
true;
1250 gLog << tr(
"Query on startup warning: No master servers are enabled in the Query menu.");
1256 if (!bGettingServers && hasCustomServers())
1261 refreshCustomServers();
1268 QMessageBox::critical(NULL, tr(
"Doomseeker critical error"),
1269 tr(
"Doomseeker was unable to find any plugin libraries.\n" 1270 "Although the application will still work it will not be possible " 1271 "to fetch any server info or launch any game.\n\n" 1272 "Please check if there are any files in \"engines/\" directory.\n" 1273 "To fix this problem you may try downloading Doomseeker " 1274 "again from the site specified in the Help|About box and reinstalling " 1279 QueryMenuAction* MainWindow::queryMenuActionForPlugin(
const EnginePlugin* plugin)
1286 if (d->queryMenuPorts.contains(plugin))
1288 return d->queryMenuPorts[plugin];
1294 void MainWindow::quitProgram()
1296 d->bWantToQuit =
true;
1297 QApplication::closeAllWindows();
1300 void MainWindow::refreshCustomServers()
1302 foreach (
const ServerPtr &server, d->serverList->servers())
1304 if (server->isCustom())
1305 gRefresher->registerServer(server.data());
1309 void MainWindow::refreshServersOnList()
1311 foreach (
const ServerPtr &server, d->serverList->servers())
1313 gRefresher->registerServer(server.data());
1317 void MainWindow::refreshThreadBeginsWork()
1319 statusBar()->showMessage(tr(
"Querying..."));
1320 d->taskbarProgress->show();
1323 void MainWindow::refreshThreadEndsWork()
1325 d->toolBarGetServers->setEnabled(
true);
1327 d->serverList->cleanUpRightNow();
1328 statusBar()->showMessage(tr(
"Done"));
1329 updateTrayIconTooltipAndLogTotalRefresh();
1330 d->taskbarProgress->hide();
1332 if (d->bTotalRefreshInProcess)
1334 initAutoRefreshTimer();
1337 d->bTotalRefreshInProcess =
false;
1338 QList<ServerPtr> selectedServers = d->serverList->selectedServers();
1339 d->detailsDock->displaySelection(selectedServers);
1342 void MainWindow::restartAndInstallUpdatesNow()
1348 void MainWindow::runGame(
const ServerPtr &server)
1350 if(d->connectionHandler)
1351 delete d->connectionHandler;
1354 d->connectionHandler->run();
1359 assert(plugin != NULL);
1361 QueryMenuAction* pAction = queryMenuActionForPlugin(plugin);
1362 if (pAction != NULL)
1364 pAction->setChecked(bEnabled);
1365 if (plugin->data()->hasMasterClient())
1367 plugin->data()->masterClient->
setEnabled(bEnabled);
1369 if (plugin->data()->hasBroadcast())
1371 plugin->data()->broadcast->setEnabled(bEnabled);
1373 d->serversStatusesWidgets[plugin]->setMasterEnabledStatus(bEnabled);
1377 void MainWindow::serverAddedToList(
const ServerPtr &pServer)
1379 if (pServer->isKnown())
1381 const QString& gameMode = pServer->gameMode().name();
1382 d->serverFilterDock->addGameModeToComboBox(gameMode);
1388 assert(this->d->updaterInstallerErrorCode == 0 &&
1389 "MainWindow::setDisplayUpdaterProcessFailure()");
1390 this->d->updaterInstallerErrorCode = errorCode;
1391 QTimer::singleShot(0,
this, SLOT(showUpdaterProcessErrorDialog()));
1396 assert(this->d->updaterInstallerErrorCode == 0 &&
1397 "MainWindow::setDisplayUpdateInstallerError()");
1398 this->d->updaterInstallerErrorCode = errorCode;
1399 QTimer::singleShot(0,
this, SLOT(showUpdateInstallErrorDialog()));
1402 void MainWindow::setupIcons()
1404 QStyle& style = *QApplication::style();
1407 d->menuActionQuit->setIcon(style.standardIcon(QStyle::SP_TitleBarCloseButton));
1410 d->menuActionHelp->setIcon(style.standardIcon(QStyle::SP_MessageBoxQuestion));
1411 d->menuActionAbout->setIcon(style.standardIcon(QStyle::SP_MessageBoxInformation));
1414 void MainWindow::setupToolBar()
1416 QToolBar* pToolBar =
new QToolBar(tr(
"Main Toolbar"),
this);
1417 pToolBar->setMovable(
false);
1418 pToolBar->setObjectName(
"Toolbar");
1421 d->toolBarGetServers =
new QAction(QIcon(
":/icons/refresh.png"), tr(
"Get Servers"), pToolBar);
1425 pToolBar->addAction(d->toolBarGetServers);
1428 pToolBar->addSeparator();
1429 pToolBar->addAction(d->menuActionCreateServer);
1430 pToolBar->addAction(d->menuActionWadseeker);
1433 pToolBar->addSeparator();
1434 pToolBar->addAction(d->menuActionManageDemos);
1435 pToolBar->addAction(d->menuActionRecordDemo);
1437 pToolBar->addSeparator();
1440 pToolBar->addAction(d->buddiesList->toggleViewAction());
1441 pToolBar->addAction(d->logDock->toggleViewAction());
1442 pToolBar->addAction(d->ircDock->toggleViewAction());
1443 pToolBar->addAction(d->serverFilterDock->toggleViewAction());
1444 pToolBar->addAction(d->detailsDock->toggleViewAction());
1447 QLineEdit *qs = d->serverFilterDock->createQuickSearch();
1448 qs->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1449 qs->setMinimumWidth(175);
1450 qs->setMaximumWidth(175);
1452 QWidget* searchSeparator =
new QWidget();
1453 searchSeparator->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
1454 pToolBar->addWidget(searchSeparator);
1455 pToolBar->addWidget(
new QLabel(tr(
"Search:"), pToolBar));
1456 pToolBar->addWidget(qs);
1458 this->addToolBar(Qt::TopToolBarArea, pToolBar);
1459 setUnifiedTitleAndToolBarOnMac(
true);
1460 connect(pToolBar, SIGNAL( actionTriggered(QAction*) ),
this, SLOT( toolBarAction(QAction*) ) );
1463 void MainWindow::showAndLogStatusMessage(
const QString &message)
1466 statusBar()->showMessage(message);
1469 void MainWindow::showEvent(QShowEvent *event)
1471 #if QT_VERSION >= 0x050000 1473 d->taskbarButton->setWindow(windowHandle());
1477 void MainWindow::showInstallFreedoomDialog()
1479 if (!d->freedoomDialog.isNull())
1481 d->freedoomDialog->activateWindow();
1485 dialog->setAttribute(Qt::WA_DeleteOnClose);
1487 d->freedoomDialog = dialog;
1490 void MainWindow::showProgramArgsHelp()
1493 dialog->setAttribute(Qt::WA_DeleteOnClose);
1497 void MainWindow::showServerJoinCommandLine(
const ServerPtr &server)
1501 GameDemo demo = gConfig.doomseeker.bRecordDemo ? GameDemo::Unmanaged : GameDemo::NoDemo;
1503 this->connect(builder, SIGNAL(commandLineBuildFinished()), SLOT(showServerJoinCommandLineOnBuilderFinished()));
1507 void MainWindow::showServerJoinCommandLineOnBuilderFinished()
1513 QString execPath = cli.
executable.absoluteFilePath();
1514 QStringList args = cli.
args;
1519 CopyTextDlg ctd(execPath +
" " + args.join(
" "), builder->server()->name(),
this);
1524 if (!builder->error().isEmpty())
1526 QMessageBox::critical(
this, tr(
"Doomseeker - show join command line"),
1527 tr(
"Command line cannot be built:\n%1").arg(builder->error()));
1530 builder->deleteLater();
1533 void MainWindow::showUpdaterProcessErrorDialog()
1535 QString explanation;
1536 if (this->d->updaterInstallerErrorCode != UpdateInstaller::PEC_GeneralFailure)
1538 QString errorCodeExplanation = UpdateInstaller::processErrorCodeToStr(
1540 explanation = tr(
"Update installation problem:\n%1").arg(errorCodeExplanation);
1544 explanation = tr(
"Update installation failed.");
1546 QMessageBox::critical(
this, tr(
"Doomseeker - Auto Update problem"),
1547 tr(
"%1\n\nRemaining updates have been discarded.").arg(explanation));
1550 void MainWindow::showUpdateInstallErrorDialog()
1552 QString msg = tr(
"Update install problem:\n%1\n\nRemaining updates have been discarded.")
1553 .arg(UpdateInstaller::errorCodeToStr(
1555 QMessageBox::critical(
this, tr(
"Doomseeker - Auto Update problem"), msg);
1558 void MainWindow::stopAutoRefreshTimer()
1560 d->autoRefreshTimer.stop();
1565 QueryMenuAction* pAction = queryMenuActionForPlugin(plugin);
1566 assert(pAction != NULL);
1568 setQueryPluginEnabled(plugin, !pAction->isChecked());
1571 void MainWindow::toolBarAction(QAction* pAction)
1573 if (pAction == d->toolBarGetServers)
1579 void MainWindow::trayIcon_activated(QSystemTrayIcon::ActivationReason reason)
1581 if (reason == QSystemTrayIcon::Trigger)
1583 if (isMinimized() || !isVisible())
1585 d->bWasMaximized ==
true ? showMaximized() : showNormal();
1588 else if (gConfig.doomseeker.bCloseToTrayIcon)
1599 void MainWindow::updateDynamicAppearance()
1601 d->tableServers->setShowGrid(gConfig.doomseeker.bDrawGridInServerTable);
1602 d->serverList->redraw();
1603 d->serverList->cleanUpForce();
1608 void MainWindow::updateMasterAddresses()
1610 for(
int i = 0;i < d->masterManager->numMasters();i++)
1612 (*d->masterManager)[i]->updateAddress();
1618 d->serverList->applyFilter(filterInfo);
1627 count += status->count();
1632 void MainWindow::updateRefreshProgress()
1635 d->taskbarProgress->setMaximum(count.numServers);
1636 d->taskbarProgress->setValue(count.numServers - count.numRefreshing);
1637 updateTrayIconTooltip(count);
1642 if (d->trayIcon != NULL)
1645 tip += tr(
"Generic servers: %1\n").arg(count.numGenericServers);
1646 tip += tr(
"Custom servers: %1\n").arg(count.numCustomServers);
1647 tip += tr(
"LAN servers: %1\n").arg(count.numLanServers);
1648 tip += tr(
"Human players: %1").arg(count.numHumanPlayers);
1649 d->trayIcon->setToolTip(tip);
1653 void MainWindow::updateTrayIconTooltipAndLogTotalRefresh()
1656 updateTrayIconTooltip(count);
1658 if (d->bTotalRefreshInProcess)
1660 gLog << tr(
"Finished refreshing. Servers on the list: %1 " 1661 "(+%2 custom, +%3 LAN). Players: %4.")
1662 .arg(count.numGenericServers).arg(count.numCustomServers)
1663 .arg(count.numLanServers).arg(count.numHumanPlayers);
static void loadImages(const QString &style)
Structure describing server filter.
void checkForUpdatesAuto()
Auto triggered updates will display install confirmation only if configured to.
Performs a case-insensitive (OS independent) file searches.
Dockable widget designed for IRC communication.
Dockable widget designed for application's log presentation.
static UpdateChannel fromName(const QString &name)
Creates object from its internal name.
void togglePluginQueryEnabled(const EnginePlugin *plugin)
Structure holding parameters for application launch.
Message object used to pass messages throughout the Doomseeker's system.
void finishConfiguration(DoomseekerConfigurationDialog &, bool)
QStringList args
launch parameters
static bool bInstallUpdatesAndRestart
If true then program will install updates and restart instead of quitting if quit is requested...
bool isFilteringAnything() const
Informs if filter will apply to any server.
QVariant value() const
Extracts the value as QVariant.
Update is fully automatic.
ProcessErrorCode
"--update-failed" values.
static bool rmAllFiles(const QString &dirPath, const QStringList &nameFilters=QStringList())
Deletes all files in specified directory.
void setQueryPluginEnabled(const EnginePlugin *pClient, bool bEnabled)
Sets query for selected MasterClient object to enabled or disabled.
Generates command line for joining specified server.
QString revision
Revision used for version comparison.
void checkForUpdatesUserTriggered()
User triggered updates will always display install confirmation.
IniVariable retrieveSetting(const QString &name)
Gets a variable but only if it already exists.
static bool isUrlSafe(const QString &url)
Unsafe URLs begin with file:// and this functions returns false for such URLs.
unsigned timestamp() const
Generation time in seconds since UTC epoch.
Dialog window allowing user to create a game.
void setDisplayUpdaterProcessFailure(int errorCode)
This should be set if program was started with "--update-failed" argument.
bool isAnythingToRefresh() const
Will check if refresh operation has any sense.
virtual const EnginePlugin * plugin() const =0
Platform-agnostic wrapper for QWinTaskbarProgress.
Deals with program updates/upgrades.
void obtainJoinCommandLine()
Runs asynchronously and emits commandLineBuildFinished() when done.
void setWads(const QStringList &wads)
Sets WADs to seek.
void fillQueryMenu(MasterManager *masterManager)
Wadseeker dialog box, only one instance is allowed.
Dialog for managing demos recorded through Doomseeker.
Manager class for a number of MasterClient instances.
INI section representation.
static void escapeExecutable(QString &arg)
Escapes the executable path and handles OS X bundles.
void setDisplayUpdateInstallerError(int errorCode)
If set then MainWindow will display the reason for updater failure.
Wrapper for PathFinder that specializes in findings WADs.
QString contents() const
Customized displayable contents of this Message.
Abstract base for all MasterClients.
static void escapeArgs(QStringList &args)
Escapes all characters in all strings on the list.
QFileInfo executable
path to the executable
QString name
Name of the package (program name or plugin name).
const QString & name() const
File name of the WAD.
bool isValid() const
It's valid when at least executable is set.
void initAutoRefreshTimer()