23 #include "gui/wadseekerinterface.h"
24 #include "ui_wadseekerinterface.h"
26 #include "application.h"
27 #include "configuration/doomseekerconfig.h"
28 #include "gui/commongui.h"
29 #include "gui/helpers/taskbarbutton.h"
30 #include "gui/helpers/taskbarprogress.h"
31 #include "mainwindow.h"
32 #include "serverapi/server.h"
34 #include "strings.hpp"
35 #include "templatedpathresolver.h"
36 #include "wadseeker/entities/checksum.h"
37 #include "wadseeker/entities/modfile.h"
38 #include "wadseeker/entities/modset.h"
40 #include <QMessageBox>
41 #include <QPushButton>
43 const int WadseekerInterface::UPDATE_INTERVAL_MS = 500;
46 DClass<WadseekerInterface> :
public Ui::WadseekerInterface
49 QPushButton *btnAbort;
50 QPushButton *btnClose;
52 bool bCompletedSuccessfully;
67 WadseekerInterface::WadseekerInterface(ServerPtr server, QWidget *parent)
73 d->lblTop->setText(tr(
"Downloading WADs for server \"%1\"").arg(server->name()));
74 setCustomSites(server->allWebSites());
77 WadseekerInterface::~WadseekerInterface()
79 currentInstance =
nullptr;
82 void WadseekerInterface::abortService(
const QString &service)
84 message(tr(
"Aborting service: %1").arg(service), WadseekerLib::Notice);
85 wadseeker.skipService(service);
88 void WadseekerInterface::abortSite(
const QUrl &url)
90 message(tr(
"Aborting site: %1").arg(url.toString()), WadseekerLib::Notice);
91 wadseeker.skipSiteSeek(url);
94 void WadseekerInterface::accept()
98 if (d->bCompletedSuccessfully)
99 done(QDialog::Accepted);
103 if (d->leWadName->text().isEmpty())
108 QStringList pwadNames = d->leWadName->text().split(
',', Qt::SkipEmptyParts);
109 for (QString pwadName : pwadNames)
111 seekedWads << pwadName.trimmed();
114 startSeeking(seekedWads);
118 void WadseekerInterface::allDone(
bool bSuccess)
121 d->bCompletedSuccessfully = bSuccess;
122 QApplication::alert(
this);
125 displayMessage(tr(
"All done. Success."), WadseekerLib::NoticeImportant,
false);
127 if (isAutomatic() && !d->preventGame)
129 if (isActiveWindow())
130 done(QDialog::Accepted);
132 d->btnStartGame->show();
137 QList<PWad> failures = unsuccessfulWads();
139 for (
const PWad &failure : failures)
141 d->twWads->setFileFailed(failure.name());
144 displayMessage(tr(
"All done. Fail."), WadseekerLib::CriticalError,
false);
148 void WadseekerInterface::connectWadseekerObject()
151 this->connect(&wadseeker, SIGNAL(allDone(
bool)),
152 SLOT(allDone(
bool)));
153 this->connect(&wadseeker, SIGNAL(message(
const QString&,WadseekerLib::MessageType)),
154 SLOT(message(
const QString&,WadseekerLib::MessageType)));
155 this->connect(&wadseeker, SIGNAL(seekStarted(
const ModSet&)),
156 SLOT(seekStarted(
const ModSet&)));
157 this->connect(&wadseeker, SIGNAL(fileInstalled(
const ModFile&)),
158 SLOT(fileDownloadSuccessful(
const ModFile&)));
159 this->connect(&wadseeker, SIGNAL(siteFinished(
const QUrl&)),
160 SLOT(siteFinished(
const QUrl&)));
161 this->connect(&wadseeker, SIGNAL(siteProgress(
const QUrl&,qint64,qint64)),
162 SLOT(siteProgress(
const QUrl&,qint64,qint64)));
163 this->connect(&wadseeker, SIGNAL(siteRedirect(
const QUrl&,
const QUrl&)),
164 SLOT(siteRedirect(
const QUrl&,
const QUrl&)));
165 this->connect(&wadseeker, SIGNAL(siteStarted(
const QUrl&)),
166 SLOT(siteStarted(
const QUrl&)));
167 this->connect(&wadseeker, SIGNAL(serviceStarted(QString)),
168 SLOT(serviceStarted(QString)));
169 this->connect(&wadseeker, SIGNAL(serviceFinished(QString)),
170 SLOT(serviceFinished(QString)));
173 d->twWads->connect(&wadseeker, SIGNAL(fileDownloadFinished(
const ModFile&)),
174 SLOT(setFileDownloadFinished(
const ModFile&)));
175 d->twWads->connect(&wadseeker, SIGNAL(fileDownloadProgress(
const ModFile&,qint64,qint64)),
176 SLOT(setFileProgress(
const ModFile&,qint64,qint64)));
177 d->twWads->connect(&wadseeker, SIGNAL(fileDownloadStarted(
const ModFile&,
const QUrl&)),
178 SLOT(setFileUrl(
const ModFile&,
const QUrl&)));
181 void WadseekerInterface::construct()
185 d->preventGame =
false;
186 d->bCompletedSuccessfully =
false;
187 d->btnAbort = d->buttonBox->button(QDialogButtonBox::Abort);
188 d->btnClose = d->buttonBox->button(QDialogButtonBox::Close);
192 d->taskbarProgress = d->taskbarButton->progress();
193 d->taskbarProgress->setMaximum(d->pbOverallProgress->maximum());
199 this->setWindowIcon(QIcon(
":/icon.png"));
200 d->btnStartGame->hide();
201 this->connect(&updateTimer, SIGNAL(timeout()), SLOT(registerUpdateRequest()));
203 connectWadseekerObject();
206 connect(d->twWads, &QWidget::customContextMenuRequested,
207 this, &WadseekerInterface::showWadsTableContextMenu);
212 QStringList urlList = gConfig.wadseeker.searchURLs;
213 if (gConfig.wadseeker.bAlwaysUseDefaultSites)
215 for (
int i = 0; !Wadseeker::defaultSites[i].isEmpty(); ++i)
216 urlList << Wadseeker::defaultSites[i];
219 wadseeker.setPrimarySites(urlList);
221 updateTimer.setSingleShot(
false);
222 updateTimer.start(UPDATE_INTERVAL_MS);
227 if (!isInstantiated())
230 return currentInstance;
237 if (!isInstantiated())
240 return currentInstance;
248 if (interface !=
nullptr)
250 interface->setupAutomatic();
251 interface->d->preventGame =
true;
256 void WadseekerInterface::displayMessage(
const QString &message, WadseekerLib::MessageType type,
bool bPrependErrorsWithMessageType)
258 QString strProcessedMessage;
260 bool bPrependWithNewline =
false;
261 QString wrapHtmlLeft =
"<div style=\"%1\">";
262 QString wrapHtmlRight =
"</div>";
267 case WadseekerLib::CriticalError:
268 htmlStyle = QString(
"color: %1; font-weight: bold;").arg(colorHtmlMessageFatalError);
269 bPrependWithNewline =
true;
271 if (bPrependErrorsWithMessageType)
272 strProcessedMessage = tr(
"CRITICAL ERROR: %1").arg(message);
274 strProcessedMessage = message;
279 case WadseekerLib::Error:
280 htmlStyle = QString(
"color: %1;").arg(colorHtmlMessageError);
282 if (bPrependErrorsWithMessageType)
283 strProcessedMessage = tr(
"Error: %1").arg(message);
285 strProcessedMessage = message;
288 case WadseekerLib::Notice:
289 htmlStyle = QString(
"color: %1;").arg(colorHtmlMessageNotice);
291 strProcessedMessage = message;
294 case WadseekerLib::NoticeImportant:
295 htmlStyle = QString(
"color: %1; font-weight: bold;").arg(colorHtmlMessageNotice);
296 bPrependWithNewline =
true;
298 strProcessedMessage = message;
302 if (bPrependWithNewline && !d->teWadseekerOutput->toPlainText().isEmpty())
303 strProcessedMessage =
"<br>" + strProcessedMessage;
305 wrapHtmlLeft = wrapHtmlLeft.arg(htmlStyle);
307 strProcessedMessage = wrapHtmlLeft + strProcessedMessage + wrapHtmlRight;
309 d->teWadseekerOutput->append(strProcessedMessage);
312 void WadseekerInterface::fileDownloadSuccessful(
const ModFile &filename)
314 successfulWads << filename;
315 d->twWads->setFileSuccessful(filename.fileName());
318 void WadseekerInterface::initMessageColors()
320 colorHtmlMessageNotice = gConfig.wadseeker.colorMessageNotice;
321 colorHtmlMessageError = gConfig.wadseeker.colorMessageError;
322 colorHtmlMessageFatalError = gConfig.wadseeker.colorMessageCriticalError;
325 bool WadseekerInterface::isInstantiated()
327 return currentInstance !=
nullptr;
330 void WadseekerInterface::message(
const QString &message, WadseekerLib::MessageType type)
332 displayMessage(message, type,
true);
335 void WadseekerInterface::registerUpdateRequest()
341 void WadseekerInterface::reject()
350 this->done(Rejected);
355 void WadseekerInterface::resetTitleToDefault()
357 setWindowTitle(tr(
"Wadseeker"));
360 void WadseekerInterface::seekStarted(
const ModSet &filenames)
364 for (ModFile modFile : filenames.modFiles())
367 names << modFile.fileName();
369 d->teWadseekerOutput->clear();
370 d->pbOverallProgress->setValue(0);
371 d->taskbarProgress->setValue(0);
372 displayMessage(
"Seek started on filenames: " + names.join(
", "), WadseekerLib::Notice,
false);
375 successfulWads.clear();
376 d->twSites->setRowCount(0);
377 d->twWads->setRowCount(0);
378 setStateDownloading();
380 for (
const PWad &wad : seekedWads)
382 d->twWads->addFile(wad.name());
386 void WadseekerInterface::setStateDownloading()
390 d->btnDownload->setEnabled(
false);
391 d->taskbarProgress->show();
395 void WadseekerInterface::setStateWaiting()
399 d->btnDownload->setEnabled(
true);
400 d->taskbarProgress->hide();
404 void WadseekerInterface::setupAutomatic()
408 d->btnDownload->hide();
409 d->leWadName->hide();
418 for (
PWad wad : wads)
422 d->leWadName->setText(names.join(
", "));
426 void WadseekerInterface::setupIdgames()
428 wadseeker.setIdgamesEnabled(gConfig.wadseeker.bSearchInIdgames);
429 wadseeker.setIdgamesUrl(gConfig.wadseeker.idgamesURL);
432 void WadseekerInterface::showEvent(QShowEvent *event)
437 d->taskbarButton->setWindow(windowHandle());
441 startSeeking(seekedWads);
445 void WadseekerInterface::serviceStarted(
const QString &service)
447 d->twSites->addService(service);
450 void WadseekerInterface::serviceFinished(
const QString &service)
452 d->twSites->removeService(service);
455 void WadseekerInterface::siteFinished(
const QUrl &site)
457 d->twSites->removeUrl(site);
458 displayMessage(
"Site finished: " + site.toString(), WadseekerLib::Notice,
false);
461 void WadseekerInterface::siteProgress(
const QUrl &site, qint64 bytes, qint64 total)
463 d->twSites->setUrlProgress(site, bytes, total);
466 void WadseekerInterface::siteRedirect(
const QUrl &oldUrl,
const QUrl &newUrl)
468 d->twSites->removeUrl(oldUrl);
469 d->twSites->addUrl(newUrl);
470 displayMessage(
"Site redirect: " + oldUrl.toString() +
" -> " + newUrl.toString(), WadseekerLib::Notice,
false);
473 void WadseekerInterface::siteStarted(
const QUrl &site)
475 d->twSites->addUrl(site);
476 displayMessage(
"Site started: " + site.toString(), WadseekerLib::Notice,
false);
479 void WadseekerInterface::startSeeking(
const QList<PWad> &seekedFilesList)
481 if (seekedFilesList.isEmpty())
483 d->bCompletedSuccessfully =
false;
486 for (
PWad seekedFile : seekedFilesList)
488 listWads.addModFile(seekedFile);
494 wadseeker.setCustomSites(customSites);
495 wadseeker.setMaximumConcurrentSeeks(gConfig.wadseeker.maxConcurrentSiteDownloads);
496 wadseeker.setMaximumConcurrentDownloads(gConfig.wadseeker.maxConcurrentWadDownloads);
497 wadseeker.startSeek(listWads);
500 void WadseekerInterface::updateProgressBar()
502 double totalPercentage = d->twWads->totalDonePercentage();
503 auto progressBarValue = (unsigned)(totalPercentage * 100.0);
505 d->pbOverallProgress->setValue(progressBarValue);
506 d->taskbarProgress->setValue(progressBarValue);
509 void WadseekerInterface::updateTitle()
515 double totalPercentage = d->twWads->totalDonePercentage();
516 if (totalPercentage < 0.0)
517 totalPercentage = 0.0;
519 setWindowTitle(tr(
"[%1%] Wadseeker").arg(totalPercentage, 6,
'f', 2));
525 resetTitleToDefault();
530 QList<PWad> WadseekerInterface::unsuccessfulWads()
const
532 QList<PWad> allWads = seekedWads;
533 for (
PWad successfulWad : successfulWads)
535 for (
int i = 0; i < allWads.size(); ++i)
537 if (allWads[i].name() == successfulWad.name())
547 void WadseekerInterface::showWadsTableContextMenu(
const QPoint &position)
549 const QModelIndex index = d->twWads->indexAt(position);
553 QString fileName = d->twWads->fileNameAtRow(index.row());
554 if (!wadseeker.isDownloadingFile(fileName))
555 menu->actionSkipCurrentSite->setEnabled(
false);
557 QAction *pResult = menu->exec();
559 if (pResult == menu->actionSkipCurrentSite)
561 QString wadName = d->twWads->fileNameAtRow(index.row());
562 d->twWads->setFileUrl(fileName, QUrl());
564 wadseeker.skipFileCurrentUrl(wadName);
566 else if (pResult !=
nullptr)
567 QMessageBox::warning(
this, tr(
"Context menu error"), tr(
"Unknown action selected."));