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/ip2cupdatebox.h"
38 #include "gui/logdock.h"
39 #include "gui/mainwindow.h"
40 #include "gui/programargshelpdialog.h"
41 #include "gui/serverdetailsdock.h"
42 #include "gui/serverfilterdock.h"
43 #include "gui/serverlist.h"
44 #include "gui/wadseekerinterface.h"
45 #include "gui/wadseekershow.h"
46 #include "ip2c/ip2cloader.h"
47 #include "irc/configuration/chatnetworkscfg.h"
48 #include "irc/configuration/ircconfig.h"
49 #include "pathfinder/pathfinder.h"
50 #include "plugins/engineplugin.h"
51 #include "plugins/pluginloader.h"
52 #include "refresher/refresher.h"
53 #include "serverapi/gameclientrunner.h"
54 #include "serverapi/mastermanager.h"
55 #include "serverapi/message.h"
56 #include "serverapi/server.h"
57 #include "updater/autoupdater.h"
58 #include "updater/updatechannel.h"
59 #include "updater/updateinstaller.h"
60 #include "updater/updatepackage.h"
61 #include "apprunner.h"
62 #include "commandline.h"
63 #include "connectionhandler.h"
64 #include "customservers.h"
65 #include "doomseekerfilepaths.h"
66 #include "fileutils.h"
68 #include "joincommandlinebuilder.h"
69 #include "pathfinder/wadpathfinder.h"
73 #include "ui_mainwindow.h"
76 #include <QApplication>
77 #include <QDesktopServices>
78 #include <QDesktopWidget>
79 #include <QDockWidget>
81 #include <QHeaderView>
84 #include <QMessageBox>
85 #include <QProgressBar>
87 #include <QSizePolicy>
89 const QString MainWindow::HELP_SITE_URL =
"http://doomseeker.drdteam.org/help";
98 class QQueryMenuAction :
public QAction
104 this->pClient = mClient;
108 connect(
this, SIGNAL( toggled(
bool) ), mClient, SLOT( setEnabled(
bool) ) );
109 connect(
this, SIGNAL( toggled(
bool) ), statusWidget, SLOT( setMasterEnabledStatus(
bool) ) );
122 DClass<MainWindow> :
public Ui::MainWindowWnd
125 PrivData() : bTotalRefreshInProcess(false), buddiesList(NULL),
126 bWasMaximized(false), bWantToQuit(false), logDock(NULL),
127 masterManager(NULL), trayIcon(NULL), trayIconMenu(NULL)
131 QApplication* application;
132 QTimer autoRefreshTimer;
135 QWidget* autoUpdaterStatusBarWidget;
136 QPushButton* autoUpdaterAbortButton;
137 QLabel* autoUpdaterLabel;
138 QProgressBar* autoUpdaterFileProgressBar;
139 QProgressBar* autoUpdaterOverallProgressBar;
145 bool bTotalRefreshInProcess;
162 QProgressBar* ip2cUpdateProgressBar;
170 QHash<MasterClient*, QQueryMenuAction*> queryMenuPorts;
171 QHash<MasterClient*, ServersStatusWidget*> serversStatusesWidgets;
172 QAction* toolBarGetServers;
173 QSystemTrayIcon* trayIcon;
177 int updaterInstallerErrorCode;
180 QDockWidget *mainDock;
187 d->autoUpdater = NULL;
189 d->connectionHandler = NULL;
191 d->updaterInstallerErrorCode = 0;
193 d->application = application;
195 this->setAttribute(Qt::WA_DeleteOnClose,
true);
199 initAutoUpdaterWidgets();
201 d->updatesConfirmationWidget->hide();
202 d->updatesDownloadedWidget->hide();
205 #ifndef WITH_AUTOUPDATES
206 d->menuActionCheckForUpdates->setVisible(
false);
209 if (gPlugins->numPlugins() == 0)
211 QString message = tr(
213 "Doomseeker failed to detect any plugins.\n"
214 "While the core application will still work its functionality is going to be limited.\n"
215 "One of the proper locations for plugin modules is the \"engines/\" directory.\n"
218 QMessageBox::critical(NULL, tr(
"Doomseeker - plugin load failure"), message);
225 d->menuView->addAction(d->buddiesList->toggleViewAction());
226 d->buddiesList->toggleViewAction()->setText(tr(
"&Buddies"));
227 d->buddiesList->toggleViewAction()->setShortcut(tr(
"CTRL+B"));
229 connect(d->buddiesList, SIGNAL(joinServer(ServerPtr)),
this, SLOT(runGame(ServerPtr)));
230 d->buddiesList->hide();
231 this->addDockWidget(Qt::LeftDockWidgetArea, d->buddiesList);
234 initServerFilterDock();
236 splitDockWidget(d->mainDock, d->serverFilterDock, Qt::Horizontal);
242 initServerDetailsDock();
243 tabifyDockWidget(d->ircDock, d->detailsDock);
246 d->menuActionRecordDemo->setChecked(gConfig.doomseeker.bRecordDemo);
250 d->buddiesList->scan(d->masterManager);
251 connect(d->masterManager, SIGNAL( masterMessage(
MasterClient*,
const QString&,
const QString&,
bool) ),
252 this, SLOT( masterManagerMessages(
MasterClient*,
const QString&,
const QString&,
bool) ) );
257 fillQueryMenu(d->masterManager);
260 d->masterManager->customServs()->readConfig(d->serverTableHandler,
261 SLOT(serverUpdated(ServerPtr,
int)),
262 SLOT(serverBegunRefreshing(ServerPtr)) );
264 setWindowIcon(QIcon(
":/icon.png"));
267 initAutoRefreshTimer();
268 connect(&d->autoRefreshTimer, SIGNAL( timeout() ),
this, SLOT( autoRefreshTimer_timeout() ));
276 int slotStyle = gConfig.doomseeker.slotStyle;
281 connectIP2CLoader(d->ip2cLoader);
282 d->ip2cLoader->load();
284 restoreState(QByteArray::fromBase64(gConfig.doomseeker.mainWindowState.toAscii()));
285 restoreGeometry(gConfig.doomseeker.mainWindowGeometry);
289 QTimer::singleShot(1,
this, SLOT(postInitAppStartup()));
292 MainWindow::~MainWindow()
295 gConfig.doomseeker.mainWindowGeometry = saveGeometry();
296 gConfig.doomseeker.mainWindowState = saveState().toBase64();
298 if (d->updateChannelOnUpdateStart != NULL)
300 delete d->updateChannelOnUpdateStart;
302 if (d->autoUpdater != NULL)
304 d->autoUpdater->disconnect();
305 delete d->autoUpdater;
307 if(d->connectionHandler)
308 delete d->connectionHandler;
310 QList<QAction*> menuQueryActions = d->menuQuery->actions();
311 QList<QAction*>::iterator it;
312 foreach (QAction *action, menuQueryActions)
314 QString pluginName = action->text();
316 if (!pluginName.isEmpty())
318 IniSection pluginConfig = gConfig.iniSectionForPlugin(pluginName);
319 pluginConfig[
"Query"] = action->isChecked();
323 if (d->trayIcon != NULL)
325 d->trayIcon->setVisible(
false);
330 if (d->trayIconMenu != NULL)
332 delete d->trayIconMenu;
333 d->trayIconMenu = NULL;
336 delete d->serverTableHandler;
338 if(d->masterManager != NULL)
340 delete d->masterManager;
343 if (d->ip2cLoader != NULL)
345 delete d->ip2cLoader;
349 void MainWindow::abortAutoUpdater()
351 if (d->autoUpdater != NULL)
353 d->autoUpdater->abort();
357 void MainWindow::autoRefreshTimer_timeout()
359 if (gConfig.doomseeker.bQueryAutoRefreshDontIfActive && !isMinimized())
361 if (QApplication::activeWindow() != 0)
370 void MainWindow::blockRefreshButtons()
372 d->toolBarGetServers->setEnabled(
false);
377 return d->buddiesList;
380 void MainWindow::changeEvent(QEvent* event)
382 if (event->type() == QEvent::ActivationChange && isActiveWindow() && !isMinimized() && !isHidden())
384 d->serverTableHandler->cleanUp();
387 QMainWindow::changeEvent(event);
390 void MainWindow::checkForUpdates(
bool bUserTriggered)
392 if (d->autoUpdater != NULL)
394 if (d->autoUpdater->isRunning())
396 QMessageBox::warning(
this, tr(
"Doomseeker - Auto Update"),
397 tr(
"Update is already in progress."));
402 delete d->autoUpdater;
403 d->autoUpdater = NULL;
406 gLog << tr(
"Removing old update packages from local temporary space.");
407 QStringList removeFilter(QString(
"%1*").arg(DataPaths::UPDATE_PACKAGE_FILENAME_PREFIX));
411 showAndLogStatusMessage(tr(
"Checking for updates..."));
413 this->connect(d->autoUpdater, SIGNAL(statusMessage(QString)),
414 SLOT(showAndLogStatusMessage(QString)));
415 this->connect(d->autoUpdater, SIGNAL(finished()),
416 SLOT(onAutoUpdaterFinish()));
417 this->connect(d->autoUpdater, SIGNAL(downloadAndInstallConfirmationRequested()),
418 SLOT(onAutoUpdaterDownloadAndInstallConfirmationRequest()));
419 this->connect(d->autoUpdater, SIGNAL(overallProgress(
int,
int,
const QString&)),
420 SLOT(onAutoUpdaterOverallProgress(
int,
int,
const QString&)));
421 this->connect(d->autoUpdater, SIGNAL(packageDownloadProgress(qint64, qint64)),
422 SLOT(onAutoUpdaterFileProgress(qint64, qint64)));
424 QMap<QString, QList<unsigned long long> > ignoredPackagesRevisions;
427 foreach (
const QString& package, gConfig.autoUpdates.lastKnownUpdateRevisions.keys())
429 unsigned long long revision = gConfig.autoUpdates.lastKnownUpdateRevisions[package];
430 QList<unsigned long long> list;
432 ignoredPackagesRevisions.insert(package, list);
435 d->autoUpdater->setIgnoreRevisions(ignoredPackagesRevisions);
438 d->autoUpdater->setChannel(channel);
439 *d->updateChannelOnUpdateStart = channel;
441 bool bRequireConfirmation =
true;
444 bRequireConfirmation = (gConfig.autoUpdates.updateMode
447 d->autoUpdater->setRequireDownloadAndInstallConfirmation(bRequireConfirmation);
448 d->autoUpdaterStatusBarWidget->show();
449 d->autoUpdater->start();
455 const bool bUserTriggered =
true;
456 checkForUpdates(!bUserTriggered);
461 const bool bUserTriggered =
true;
462 checkForUpdates(bUserTriggered);
465 void MainWindow::closeEvent(QCloseEvent* event)
470 if (d->trayIcon != NULL && gConfig.doomseeker.bCloseToTrayIcon && !d->bWantToQuit)
472 d->bWasMaximized = isMaximized();
482 void MainWindow::confirmUpdateInstallation()
484 assert(d->autoUpdater != NULL &&
"MainWindow::confirmUpdateInstallation()");
485 d->updatesConfirmationWidget->hide();
486 d->autoUpdater->confirmDownloadAndInstall();
489 void MainWindow::connectIP2CLoader(
IP2CLoader* loader)
491 this->connect(d->ip2cLoader, SIGNAL( finished() ), SLOT( ip2cJobsFinished()));
492 this->connect(d->ip2cLoader, SIGNAL( downloadProgress(qint64, qint64) ),
493 SLOT( ip2cDownloadProgress(qint64, qint64) ) );
496 void MainWindow::discardUpdates()
498 assert(d->autoUpdater != NULL &&
"MainWindow::confirmUpdateInstallation()");
499 d->updatesConfirmationWidget->hide();
502 const QList<UpdatePackage>& pkgList = d->autoUpdater->newUpdatePackages();
505 gConfig.autoUpdates.lastKnownUpdateRevisions.insert(pkg.
name, pkg.
revision);
507 d->autoUpdater->abort();
513 connect(gRefresher, SIGNAL( block() ),
this, SLOT( blockRefreshButtons() ) );
514 connect(gRefresher, SIGNAL( finishedQueryingMaster(
MasterClient*) ),
this, SLOT( finishedQueryingMaster(
MasterClient*) ) );
515 connect(gRefresher, SIGNAL( sleepingModeEnter() ),
this, SLOT( refreshThreadEndsWork() ) );
516 connect(gRefresher, SIGNAL( sleepingModeEnter() ), d->buddiesList, SLOT( scan() ) );
517 connect(gRefresher, SIGNAL( sleepingModeExit() ),
this, SLOT( refreshThreadBeginsWork() ) );
520 connect(d->menuActionAbout, SIGNAL( triggered() ),
this, SLOT( menuHelpAbout() ));
521 connect(d->menuActionAboutQt, SIGNAL( triggered() ), d->application, SLOT( aboutQt() ));
522 connect(d->menuActionBuddies, SIGNAL( triggered() ),
this, SLOT( menuBuddies() ));
523 connect(d->menuActionConfigure, SIGNAL( triggered() ),
this, SLOT( menuOptionsConfigure() ));
524 connect(d->menuActionCreateServer, SIGNAL( triggered() ),
this, SLOT( menuCreateServer() ));
525 connect(d->menuActionHelp, SIGNAL( triggered() ),
this, SLOT ( menuHelpHelp() ) );
526 connect(d->menuActionIRCOptions, SIGNAL( triggered() ),
this, SLOT( menuIRCOptions() ) );
527 connect(d->menuActionLog, SIGNAL( triggered() ),
this, SLOT( menuLog() ));
528 connect(d->menuActionManageDemos, SIGNAL( triggered() ),
this, SLOT( menuManageDemos() ) );
529 connect(d->menuActionRecordDemo, SIGNAL( triggered() ),
this, SLOT( menuRecordDemo() ) );
530 connect(d->menuActionUpdateIP2C, SIGNAL( triggered() ),
this, SLOT( menuUpdateIP2C() ) );
531 connect(d->menuActionQuit, SIGNAL( triggered() ),
this, SLOT( quitProgram() ));
532 connect(d->menuActionViewIRC, SIGNAL( triggered() ) ,
this, SLOT( menuViewIRC() ));
533 connect(d->menuActionWadseeker, SIGNAL( triggered() ),
this, SLOT( menuWadSeeker() ));
536 connect(d->serverFilterDock, SIGNAL(nonEmptyServerGroupingAtTopToggled(
bool)),
537 d->serverTableHandler, SLOT(setGroupServersWithPlayersAtTop(
bool)) );
540 connect(d->serverTableHandler, SIGNAL( serverDoubleClicked(ServerPtr) ),
this, SLOT( runGame(ServerPtr) ) );
541 connect(d->serverTableHandler, SIGNAL( displayServerJoinCommandLine(
const ServerPtr&) ),
this, SLOT( showServerJoinCommandLine(
const ServerPtr&) ) );
542 connect(d->serverTableHandler, SIGNAL( findMissingWADs(
const ServerPtr&) ),
this, SLOT( findMissingWADs(
const ServerPtr&) ) );
543 connect(d->serverTableHandler, SIGNAL( serverInfoUpdated(ServerPtr) ),
this, SLOT( serverAddedToList(ServerPtr) ) );
544 connect(d->buddiesList, SIGNAL(scanCompleted()), d->serverTableHandler, SLOT(redraw()));
551 for(
unsigned i = 0; i < gPlugins->numPlugins(); ++i)
554 if(!plugin->data()->hasMasterServer)
560 pMasterClient->updateAddress();
561 masterManager->addMaster(pMasterClient);
565 d->serversStatusesWidgets.insert(pMasterClient, statusWidget);
567 this->connect(statusWidget, SIGNAL( clicked(
MasterClient*) ) ,
570 statusBar()->addPermanentWidget(statusWidget);
572 QString name = gPlugins->info(i)->data()->name;
573 QQueryMenuAction* query =
new QQueryMenuAction(pMasterClient, statusWidget, d->menuQuery);
574 d->queryMenuPorts.insert(pMasterClient, query);
576 d->menuQuery->addAction(query);
578 query->setCheckable(
true);
579 query->setIcon(plugin->icon());
580 query->setText(name);
582 IniSection pluginConfig = gConfig.iniSectionForPlugin(name);
586 bool enabled = pluginConfig[
"Query"];
598 void MainWindow::findMissingWADs(
const ServerPtr &server)
601 bool allfound =
true;
602 QList<PWad> wads = server->wads();
603 PathFinder pathFinder = server->wadPathFinder();
605 foreach(
const PWad &wad, wads)
607 if(!wadFinder.find(wad.
name()).isValid())
615 QMessageBox::information(
this, tr(
"All WADs found"), tr(
"All of the WADs used by this server are present."));
631 updateMasterAddresses();
632 gRefresher->setDelayBetweenResends(gConfig.doomseeker.querySpeed().delayBetweenSingleServerAttempts);
635 if (configDialog.appearanceChanged())
637 d->tableServers->setShowGrid(gConfig.doomseeker.bDrawGridInServerTable);
638 d->serverTableHandler->redraw();
643 if (lookupHostsChanged)
645 d->serverTableHandler->lookupHosts();
649 if (configDialog.customServersChanged())
651 d->serverTableHandler->serverModel()->removeCustomServers();
652 d->masterManager->customServs()->readConfig(d->serverTableHandler, SLOT(serverUpdated(ServerPtr,
int)), SLOT(serverBegunRefreshing(ServerPtr)) );
653 refreshCustomServers();
657 void MainWindow::finishedQueryingMaster(
MasterClient* master)
664 for(
int i = 0;i < master->numServers();i++)
666 connect((*master)[i].data(), SIGNAL(updated(ServerPtr,
int)),
667 d->serverTableHandler, SLOT(serverUpdated(ServerPtr,
int)) );
669 connect((*master)[i].data(), SIGNAL(begunRefreshing(ServerPtr)),
670 d->serverTableHandler, SLOT(serverBegunRefreshing(ServerPtr)) );
674 void MainWindow::getServers()
679 gLog << tr(
"Senseless refresh operation attempted.");
681 QString message = tr(
"Doomseeker is unable to proceed with the refresh"
682 " operation because the following problem has occured:\n\n");
684 if (gPlugins->numPlugins() == 0)
686 message += tr(
"Plugins are missing from the \"engines/\" directory.");
688 else if (!isAnyMasterEnabled())
690 message += tr(
"No master servers are enabled in the \"Query\" menu.");
694 message += tr(
"Unknown error occured.");
698 QMessageBox::warning(
this, tr(
"Doomseeker - senseless operation"), message);
702 d->bTotalRefreshInProcess =
true;
703 d->autoRefreshTimer.stop();
704 gLog << tr(
"Total refresh process initialized!");
705 d->serverTableHandler->clearTable();
706 refreshCustomServers();
708 bool bAtLeastOneEnabled =
false;
710 if (!isAnyMasterEnabled())
712 gLog << tr(
"Warning: No master servers were enabled for this refresh. "
713 "Check your Query menu or \"engines/\" directory. Custom servers will still refresh.");
716 d->masterManager->clearServers();
717 for (
int i = 0; i < d->masterManager->numMasters(); ++i)
723 gRefresher->registerMaster(pMaster);
728 bool MainWindow::hasCustomServers()
const
730 CustomServers* customServers = d->masterManager->customServs();
731 return customServers->numServers() > 0;
736 const unsigned MIN_DELAY = 30;
737 const unsigned MAX_DELAY = 3600;
739 bool bEnabled = gConfig.doomseeker.bQueryAutoRefreshEnabled;
743 d->autoRefreshTimer.stop();
749 unsigned& delay = gConfig.doomseeker.queryAutoRefreshEverySeconds;
752 if (delay < MIN_DELAY)
756 else if (delay > MAX_DELAY)
761 unsigned delayMs = delay * 1000;
763 d->autoRefreshTimer.setSingleShot(
false);
764 d->autoRefreshTimer.start(delayMs);
768 void MainWindow::initAutoUpdaterWidgets()
770 static const int FILE_BAR_WIDTH = 50;
771 static const int OVERALL_BAR_WIDTH = 180;
773 d->autoUpdaterStatusBarWidget =
new QWidget(statusBar());
774 d->autoUpdaterStatusBarWidget->setLayout(
new QHBoxLayout(d->autoUpdaterStatusBarWidget));
775 d->autoUpdaterStatusBarWidget->layout()->setContentsMargins(QMargins(0, 0, 0, 0));
776 statusBar()->addPermanentWidget(d->autoUpdaterStatusBarWidget);
777 d->autoUpdaterStatusBarWidget->hide();
779 d->autoUpdaterLabel =
new QLabel(d->autoUpdaterStatusBarWidget);
780 d->autoUpdaterLabel->setText(tr(
"Auto Updater:"));
781 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterLabel);
783 d->autoUpdaterFileProgressBar = mkStdProgressBarForStatusBar();
784 d->autoUpdaterFileProgressBar->setFormat(
"%p%");
785 d->autoUpdaterFileProgressBar->setMaximumWidth(FILE_BAR_WIDTH);
786 d->autoUpdaterFileProgressBar->setMinimumWidth(FILE_BAR_WIDTH);
787 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterFileProgressBar);
789 d->autoUpdaterOverallProgressBar = mkStdProgressBarForStatusBar();
790 d->autoUpdaterOverallProgressBar->setMaximumWidth(OVERALL_BAR_WIDTH);
791 d->autoUpdaterOverallProgressBar->setMinimumWidth(OVERALL_BAR_WIDTH);
792 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterOverallProgressBar);
794 d->autoUpdaterAbortButton =
new QPushButton(statusBar());
795 d->autoUpdaterAbortButton->setToolTip(tr(
"Abort update."));
796 d->autoUpdaterAbortButton->setIcon(QIcon(
":/icons/x.png"));
797 this->connect(d->autoUpdaterAbortButton, SIGNAL(clicked()),
798 SLOT(abortAutoUpdater()));
799 d->autoUpdaterStatusBarWidget->layout()->addWidget(d->autoUpdaterAbortButton);
802 void MainWindow::initIP2CUpdater()
804 static const int PROGRESSBAR_WIDTH = 220;
806 d->ip2cUpdateProgressBar = mkStdProgressBarForStatusBar();
807 d->ip2cUpdateProgressBar->setFormat(tr(
"IP2C Update"));
808 d->ip2cUpdateProgressBar->hide();
809 d->ip2cUpdateProgressBar->setMaximumWidth(PROGRESSBAR_WIDTH);
810 d->ip2cUpdateProgressBar->setMinimumWidth(PROGRESSBAR_WIDTH);
811 statusBar()->addPermanentWidget(d->ip2cUpdateProgressBar);
814 void MainWindow::initIRCDock()
816 d->ircDock =
new IRCDock(
this);
817 d->menuView->addAction(d->ircDock->toggleViewAction());
818 d->ircDock->toggleViewAction()->setText(tr(
"&IRC"));
819 d->ircDock->toggleViewAction()->setShortcut(tr(
"CTRL+I"));
821 this->addDockWidget(Qt::BottomDockWidgetArea, d->ircDock);
825 this->d->ircDock->setVisible(
true);
826 this->d->ircDock->performNetworkAutojoins();
830 void MainWindow::initLogDock()
832 d->logDock =
new LogDock(
this);
833 d->menuView->addAction(d->logDock->toggleViewAction());
834 d->logDock->toggleViewAction()->setText(tr(
"&Log"));
835 d->logDock->toggleViewAction()->setShortcut(tr(
"CTRL+L"));
837 this->addDockWidget(Qt::BottomDockWidgetArea, d->logDock);
839 connect(&gLog, SIGNAL( newEntry(
const QString&) ), d->logDock, SLOT( appendLogEntry(
const QString&) ) );
842 d->logDock->appendLogEntry(gLog.content());
845 void MainWindow::initMainDock()
847 setDockNestingEnabled(
true);
851 d->mainDock =
new QDockWidget(tr(
"Servers"));
852 d->mainDock->setTitleBarWidget(
new QWidget(
this));
853 d->mainDock->setObjectName(
"ServerList");
854 d->mainDock->setFeatures(QDockWidget::NoDockWidgetFeatures);
855 d->mainDock->setWidget(centralWidget());
857 addDockWidget(Qt::RightDockWidgetArea, d->mainDock);
860 void MainWindow::initServerDetailsDock()
863 d->menuView->addAction(d->detailsDock->toggleViewAction());
864 d->detailsDock->toggleViewAction()->setText(tr(
"Server &Details"));
865 d->detailsDock->toggleViewAction()->setShortcut(tr(
"CTRL+D"));
866 d->detailsDock->hide();
867 addDockWidget(Qt::BottomDockWidgetArea, d->detailsDock);
869 d->detailsDock->connect(d->serverTableHandler, SIGNAL( serversSelected(QList<ServerPtr>&) ), SLOT( displaySelection(QList<ServerPtr> &) ));
872 void MainWindow::initServerFilterDock()
876 d->menuView->addAction(d->serverFilterDock->toggleViewAction());
877 d->serverFilterDock->hide();
878 this->addDockWidget(Qt::RightDockWidgetArea, d->serverFilterDock);
883 bool isEnabled = gConfig.doomseeker.bUseTrayIcon;
884 if (!isEnabled || !QSystemTrayIcon::isSystemTrayAvailable())
886 if (d->trayIcon != NULL)
892 if (d->trayIconMenu != NULL)
894 delete d->trayIconMenu;
895 d->trayIconMenu = NULL;
898 else if (d->trayIcon == NULL)
901 d->trayIconMenu =
new QMenu(
this);
902 trayAction = d->trayIconMenu->addAction(
"Exit");
903 connect(trayAction, SIGNAL( triggered() ),
this, SLOT( quitProgram() ) );
906 d->trayIcon =
new QSystemTrayIcon(
this);
907 connect(d->trayIcon, SIGNAL( activated(QSystemTrayIcon::ActivationReason) ),
this, SLOT( trayIcon_activated(QSystemTrayIcon::ActivationReason) ) );
911 d->trayIcon->setContextMenu(d->trayIconMenu);
912 d->trayIcon->setIcon(QIcon(
":/icon.png"));
913 d->trayIcon->setVisible(
true);
917 void MainWindow::ip2cDownloadProgress(qint64 current, qint64 max)
919 d->ip2cUpdateProgressBar->setMaximum(max);
920 d->ip2cUpdateProgressBar->setValue(current);
923 void MainWindow::ip2cJobsFinished()
925 d->menuActionUpdateIP2C->setEnabled(
true);
926 d->serverTableHandler->updateCountryFlags();
927 d->ip2cUpdateProgressBar->hide();
929 if (d->ip2cLoader != NULL)
931 delete d->ip2cLoader;
932 d->ip2cLoader = NULL;
936 void MainWindow::ip2cStartUpdate()
938 if (d->ip2cLoader != NULL)
944 d->menuActionUpdateIP2C->setEnabled(
false);
945 d->ip2cUpdateProgressBar->show();
948 connectIP2CLoader(d->ip2cLoader);
949 d->ip2cLoader->update();
954 return hasCustomServers() || isAnyMasterEnabled();
957 bool MainWindow::isAnyMasterEnabled()
const
959 for (
int i = 0; i < d->masterManager->numMasters(); ++i)
972 void MainWindow::masterManagerMessages(
MasterClient* pSender,
const QString& title,
const QString& content,
bool isError)
974 QString message = tr(
"Master server for %1: %2").arg(pSender->
plugin()->data()->name).arg(content);
978 message = tr(
"Error: %1").arg(message);
979 statusBar()->showMessage(message);
985 void MainWindow::masterManagerMessagesImportant(
MasterClient* pSender,
const Message& objMessage)
987 QString strFullMessage = tr(
"%1: %2")
988 .arg(pSender->
plugin()->data()->name)
990 d->importantMessagesWidget->addMessage(strFullMessage, objMessage.
timestamp());
993 void MainWindow::menuBuddies()
995 d->buddiesList->setVisible(!d->buddiesList->isVisible());
998 void MainWindow::menuCreateServer()
1002 dialog->setWindowIcon(this->windowIcon());
1006 void MainWindow::menuHelpAbout()
1009 d->autoRefreshTimer.stop();
1014 void MainWindow::menuHelpHelp()
1018 QMessageBox::critical(
this, tr(
"Help error"), tr(
"No help found."), QMessageBox::Ok, QMessageBox::Ok);
1022 bool bSuccess = QDesktopServices::openUrl(HELP_SITE_URL);
1026 QMessageBox::critical(
this, tr(
"Help error"), tr(
"Failed to open URL:\n%1").arg(HELP_SITE_URL), QMessageBox::Ok, QMessageBox::Ok);
1031 void MainWindow::menuIRCOptions()
1034 dialog.initOptionsList();
1037 if (d->ircDock != NULL)
1039 d->ircDock->applyAppearanceSettings();
1044 d->ircDock->sounds().loadFromConfig();
1048 void MainWindow::menuLog()
1050 d->logDock->setVisible(!d->logDock->isVisible());
1053 void MainWindow::menuManageDemos()
1059 void MainWindow::menuOptionsConfigure()
1061 DoomseekerConfigurationDialog::openConfiguration();
1064 void MainWindow::menuRecordDemo()
1066 gConfig.doomseeker.bRecordDemo = d->menuActionRecordDemo->isChecked();
1069 void MainWindow::menuUpdateIP2C()
1073 connect(&updateBox, SIGNAL( accepted() ),
this, SLOT( ip2cStartUpdate() ) );
1078 void MainWindow::menuViewIRC()
1080 d->ircDock->setVisible(!d->ircDock->isVisible());
1083 void MainWindow::menuWadSeeker()
1085 if (gWadseekerShow->checkWadseekerValidity(
this))
1088 wadseeker->setAttribute(Qt::WA_DeleteOnClose);
1093 QProgressBar* MainWindow::mkStdProgressBarForStatusBar()
1095 QProgressBar* pBar =
new QProgressBar(statusBar());
1096 pBar->setAlignment(Qt::AlignCenter);
1097 pBar->setTextVisible(
true);
1098 pBar->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1102 void MainWindow::notifyFirstRun()
1105 QMessageBox::information(NULL, tr(
"Welcome to Doomseeker"),
1106 tr(
"Before you start browsing for servers, please ensure that Doomseeker is properly configured."));
1107 d->menuActionConfigure->trigger();
1110 void MainWindow::onAutoUpdaterDownloadAndInstallConfirmationRequest()
1112 d->updatesConfirmationWidget->show();
1115 void MainWindow::onAutoUpdaterFileProgress(qint64 bytesReceived, qint64 bytesTotal)
1117 d->autoUpdaterFileProgressBar->setValue(bytesReceived);
1118 d->autoUpdaterFileProgressBar->setMaximum(bytesTotal);
1121 void MainWindow::onAutoUpdaterFinish()
1123 showAndLogStatusMessage(tr(
"Program update detection & download finished with status: [%1] %2")
1124 .arg((
int)d->autoUpdater->errorCode()).arg(d->autoUpdater->errorString()));
1125 gConfig.autoUpdates.bPerformUpdateOnNextRun =
false;
1126 if (d->autoUpdater->errorCode() == AutoUpdater::EC_Ok)
1129 if (channel == *d->updateChannelOnUpdateStart)
1131 if (!d->autoUpdater->downloadedPackagesFilenames().isEmpty())
1133 gLog << tr(
"Updates will be installed on next program start.");
1134 d->updatesDownloadedWidget->show();
1135 gConfig.autoUpdates.bPerformUpdateOnNextRun =
true;
1140 gLog << tr(
"Update channel was changed during update process. Discarding update.");
1143 gConfig.saveToFile();
1144 d->autoUpdaterStatusBarWidget->hide();
1145 d->updatesConfirmationWidget->hide();
1146 d->autoUpdater->deleteLater();
1147 d->autoUpdater = NULL;
1150 void MainWindow::onAutoUpdaterOverallProgress(
int current,
int total,
1153 d->autoUpdaterOverallProgressBar->setValue(current);
1154 d->autoUpdaterOverallProgressBar->setMaximum(total);
1155 d->autoUpdaterOverallProgressBar->setFormat(msg);
1158 void MainWindow::postInitAppStartup()
1161 d->serverFilterDock->setFilterInfo(gConfig.serverFilter.info);
1165 if (gPlugins->numPlugins() > 0)
1167 bool bGettingServers =
false;
1168 bool queryOnStartup = gConfig.doomseeker.bQueryOnStartup;
1175 if (isAnyMasterEnabled())
1177 bGettingServers =
true;
1182 gLog << tr(
"Query on startup warning: No master servers are enabled in the Query menu.");
1188 if (!bGettingServers && hasCustomServers())
1193 refreshCustomServers();
1200 QMessageBox::critical(NULL, tr(
"Doomseeker critical error"),
1201 tr(
"Doomseeker was unable to find any plugin libraries.\n"
1202 "Although the application will still work it will not be possible "
1203 "to fetch any server info or launch any game.\n\n"
1204 "Please check if there are any files in \"engines/\" directory.\n"
1205 "To fix this problem you may try downloading Doomseeker "
1206 "again from the site specified in the Help|About box and reinstalling "
1211 QQueryMenuAction* MainWindow::queryMenuActionForMasterClient(
MasterClient* pClient)
1213 if (pClient == NULL)
1218 if (d->queryMenuPorts.contains(pClient))
1220 return d->queryMenuPorts[pClient];
1226 void MainWindow::quitProgram()
1228 d->bWantToQuit =
true;
1229 QApplication::closeAllWindows();
1232 void MainWindow::refreshCustomServers()
1234 CustomServers* customServers = d->masterManager->customServs();
1236 for(
int i = 0;i < customServers->numServers();i++)
1238 ServerPtr server = (*customServers)[i];
1240 gRefresher->registerServer(server.data());
1244 void MainWindow::refreshThreadBeginsWork()
1247 d->serverTableHandler->serverTable()->setAllowAllRowsRefresh(
false);
1248 statusBar()->showMessage(tr(
"Querying..."));
1251 void MainWindow::refreshThreadEndsWork()
1253 d->toolBarGetServers->setEnabled(
true);
1255 d->serverTableHandler->serverTable()->setAllowAllRowsRefresh(
true);
1256 d->serverTableHandler->cleanUpForce();
1257 statusBar()->showMessage(tr(
"Done"));
1260 if (d->bTotalRefreshInProcess)
1265 d->bTotalRefreshInProcess =
false;
1266 QList<ServerPtr> selectedServers = d->serverTableHandler->selectedServers();
1267 d->detailsDock->displaySelection(selectedServers);
1270 void MainWindow::restartAndInstallUpdatesNow()
1276 void MainWindow::runGame(
const ServerPtr &server)
1278 if(d->connectionHandler)
1279 delete d->connectionHandler;
1282 d->connectionHandler->run();
1287 assert(pClient != NULL);
1289 QQueryMenuAction* pAction = queryMenuActionForMasterClient(pClient);
1290 if (pAction != NULL)
1292 pAction->setChecked(bEnabled);
1294 d->serversStatusesWidgets[pClient]->setMasterEnabledStatus(bEnabled);
1298 void MainWindow::serverAddedToList(
const ServerPtr &pServer)
1300 if (pServer->isKnown())
1302 const QString& gameMode = pServer->gameMode().name();
1303 d->serverFilterDock->addGameModeToComboBox(gameMode);
1309 assert(this->d->updaterInstallerErrorCode == 0 &&
1310 "MainWindow::setDisplayUpdaterProcessFailure()");
1311 this->d->updaterInstallerErrorCode = errorCode;
1312 QTimer::singleShot(0,
this, SLOT(showUpdaterProcessErrorDialog()));
1317 assert(this->d->updaterInstallerErrorCode == 0 &&
1318 "MainWindow::setDisplayUpdateInstallerError()");
1319 this->d->updaterInstallerErrorCode = errorCode;
1320 QTimer::singleShot(0,
this, SLOT(showUpdateInstallErrorDialog()));
1323 void MainWindow::setupIcons()
1325 QStyle& style = *QApplication::style();
1328 d->menuActionQuit->setIcon(style.standardIcon(QStyle::SP_TitleBarCloseButton));
1331 d->menuActionHelp->setIcon(style.standardIcon(QStyle::SP_MessageBoxQuestion));
1332 d->menuActionAbout->setIcon(style.standardIcon(QStyle::SP_MessageBoxInformation));
1335 void MainWindow::setupToolBar()
1337 QToolBar* pToolBar =
new QToolBar(tr(
"Main Toolbar"),
this);
1338 pToolBar->setMovable(
false);
1339 pToolBar->setObjectName(
"Toolbar");
1342 d->toolBarGetServers =
new QAction(QIcon(
":/icons/refresh.png"), tr(
"Get Servers"), pToolBar);
1346 pToolBar->addAction(d->toolBarGetServers);
1349 pToolBar->addSeparator();
1350 pToolBar->addAction(d->menuActionCreateServer);
1351 pToolBar->addAction(d->menuActionWadseeker);
1354 pToolBar->addSeparator();
1355 pToolBar->addAction(d->menuActionManageDemos);
1356 pToolBar->addAction(d->menuActionRecordDemo);
1358 pToolBar->addSeparator();
1361 pToolBar->addAction(d->buddiesList->toggleViewAction());
1362 pToolBar->addAction(d->logDock->toggleViewAction());
1363 pToolBar->addAction(d->ircDock->toggleViewAction());
1364 pToolBar->addAction(d->serverFilterDock->toggleViewAction());
1365 pToolBar->addAction(d->detailsDock->toggleViewAction());
1368 QLineEdit *qs = d->serverFilterDock->createQuickSearch();
1369 qs->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1370 qs->setMinimumWidth(175);
1371 qs->setMaximumWidth(175);
1373 QWidget* searchSeparator =
new QWidget();
1374 searchSeparator->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
1375 pToolBar->addWidget(searchSeparator);
1376 pToolBar->addWidget(
new QLabel(tr(
"Search:"), pToolBar));
1377 pToolBar->addWidget(qs);
1379 this->addToolBar(Qt::TopToolBarArea, pToolBar);
1380 setUnifiedTitleAndToolBarOnMac(
true);
1381 connect(pToolBar, SIGNAL( actionTriggered(QAction*) ),
this, SLOT( toolBarAction(QAction*) ) );
1384 void MainWindow::showAndLogStatusMessage(
const QString &message)
1387 statusBar()->showMessage(message);
1390 void MainWindow::showProgramArgsHelp()
1393 dialog->setAttribute(Qt::WA_DeleteOnClose);
1397 void MainWindow::showServerJoinCommandLine(
const ServerPtr &server)
1401 GameDemo demo = gConfig.doomseeker.bRecordDemo ? GameDemo::Unmanaged : GameDemo::NoDemo;
1403 this->connect(builder, SIGNAL(commandLineBuildFinished()), SLOT(showServerJoinCommandLineOnBuilderFinished()));
1407 void MainWindow::showServerJoinCommandLineOnBuilderFinished()
1413 QString execPath = cli.
executable.absoluteFilePath();
1414 QStringList args = cli.
args;
1419 CopyTextDlg ctd(execPath +
" " + args.join(
" "), builder->server()->name(),
this);
1424 if (!builder->error().isEmpty())
1426 QMessageBox::critical(
this, tr(
"Doomseeker - show join command line"),
1427 tr(
"Command line cannot be built:\n%1").arg(builder->error()));
1430 builder->deleteLater();
1433 void MainWindow::showUpdaterProcessErrorDialog()
1435 QString explanation;
1436 if (this->d->updaterInstallerErrorCode != UpdateInstaller::PEC_GeneralFailure)
1438 QString errorCodeExplanation = UpdateInstaller::processErrorCodeToStr(
1440 explanation = tr(
"Update installation problem:\n%1").arg(errorCodeExplanation);
1444 explanation = tr(
"Update installation failed.");
1446 QMessageBox::critical(
this, tr(
"Doomseeker - Auto Update problem"),
1447 tr(
"%1\n\nRemaining updates have been discarded.").arg(explanation));
1450 void MainWindow::showUpdateInstallErrorDialog()
1452 QString msg = tr(
"Update install problem:\n%1\n\nRemaining updates have been discarded.")
1453 .arg(UpdateInstaller::errorCodeToStr(
1455 QMessageBox::critical(
this, tr(
"Doomseeker - Auto Update problem"), msg);
1458 void MainWindow::stopAutoRefreshTimer()
1460 d->autoRefreshTimer.stop();
1465 QQueryMenuAction* pAction = queryMenuActionForMasterClient(pClient);
1466 assert(pAction != NULL);
1471 void MainWindow::toolBarAction(QAction* pAction)
1473 if (pAction == d->toolBarGetServers)
1479 void MainWindow::trayIcon_activated(QSystemTrayIcon::ActivationReason reason)
1481 if (reason == QSystemTrayIcon::Trigger)
1483 if (isMinimized() || !isVisible())
1485 d->bWasMaximized ==
true ? showMaximized() : showNormal();
1488 else if (gConfig.doomseeker.bCloseToTrayIcon)
1501 void MainWindow::updateMasterAddresses()
1503 for(
int i = 0;i < d->masterManager->numMasters();i++)
1505 (*d->masterManager)[i]->updateAddress();
1512 d->serverTableHandler->applyFilter(filterInfo);
1518 int numServers = d->masterManager->numServers();
1519 int numCustoms = d->masterManager->customServs()->numServers();
1520 int numPlayers = d->masterManager->numPlayers() + d->masterManager->customServs()->numPlayers();
1522 if (d->trayIcon != NULL)
1525 tip +=
"Servers: " + QString::number(numServers) +
" + " + QString::number(numCustoms) +
" custom\n";
1526 tip +=
"Players: " + QString::number(numPlayers);
1527 d->trayIcon->setToolTip(tip);
1530 if (d->bTotalRefreshInProcess)
1532 gLog << tr(
"Finished refreshing. Servers on the list: %1 (+ %2 custom). Players: %3.")
1533 .arg(numServers).arg(numCustoms).arg(numPlayers);
void setQueryMasterServerEnabled(MasterClient *pClient, bool bEnabled)
Sets query for selected MasterClient object to enabled or disabled.
Structure describing server filter.
void checkForUpdatesAuto()
Auto triggered updates will display install confirmation only if configured to.
static void loadImages(int style)
Performs a case-insensitive (OS independent) file searches.
void updateTrayIconTooltipAndLogTotalRefresh()
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.
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
void toggleMasterClientEnabled(MasterClient *pClient)
Toggles specified MasterClient object enabled or disabled.
static bool bInstallUpdatesAndRestart
If true then program will install updates and restart instead of quitting if quit is requested...
void setPasswords(const QString &connectPassword=QString(), const QString &inGamePassword=QString())
Sets the connect/ingame password and bypasses the prompt. Set passwords to a null string to unset...
bool isFilteringAnything() const
Informs if filter will apply to any server.
QVariant value() const
Extracts the value as QVariant.
Update is fully automatic.
"Dummy" response for servers that weren't refreshed yet.
ProcessErrorCode
"--update-failed" values.
static bool rmAllFiles(const QString &dirPath, const QStringList &nameFilters=QStringList())
Deletes all files in specified directory.
virtual MasterClient * masterClient() const
Generates command line for joining specified server.
void checkForUpdatesUserTriggered()
User triggered updates will always display install confirmation.
void setRequireOptionals(bool)
Treats optional wads are required so Wadseeker prompts.
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 host 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.
Deals with program updates/upgrades.
void obtainJoinCommandLine()
Runs asynchronously and emits commandLineBuildFinished() when done.
void fillQueryMenu(MasterManager *masterManager)
virtual const EnginePlugin * plugin() const =0
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
unsigned long long revision
Revision number used for version comparison.
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()