wadseekerinterface.cpp
1 //------------------------------------------------------------------------------
2 // wadseekerinterface.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2009 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "gui/wadseekerinterface.h"
24 #include "ui_wadseekerinterface.h"
25 
26 #include "configuration/doomseekerconfig.h"
27 #include "gui/helpers/taskbarbutton.h"
28 #include "gui/helpers/taskbarprogress.h"
29 #include "serverapi/server.h"
30 #include "application.h"
31 #include "mainwindow.h"
32 #include "strings.h"
33 
34 #include <QMessageBox>
35 
36 const int WadseekerInterface::UPDATE_INTERVAL_MS = 500;
37 WadseekerInterface *WadseekerInterface::currentInstance = NULL;
38 
39 DClass<WadseekerInterface> : public Ui::WadseekerInterface
40 {
41 public:
42  bool bCompletedSuccessfully;
43  bool preventGame;
44  TaskbarButton *taskbarButton;
45  TaskbarProgress *taskbarProgress;
46 };
47 
48 DPointered(WadseekerInterface)
49 
50 WadseekerInterface::WadseekerInterface(QWidget* parent)
51 : QDialog(parent)
52 {
53  construct();
54  bAutomatic = false;
55 }
56 
57 WadseekerInterface::WadseekerInterface(ServerPtr server, QWidget* parent)
58 : QDialog(parent)
59 {
60  construct();
61  setupAutomatic();
62  d->lblTop->show();
63  d->lblTop->setText(tr("Downloading WADs for server \"%1\"").arg(server->name()));
64  setCustomSite(server->webSite());
65 }
66 
67 WadseekerInterface::~WadseekerInterface()
68 {
69  currentInstance = NULL;
70 }
71 
72 void WadseekerInterface::abortService(const QString &service)
73 {
74  message(tr("Aborting service: %1").arg(service), WadseekerLib::Notice);
75  wadseeker.skipService(service);
76 }
77 
78 void WadseekerInterface::abortSite(const QUrl &url)
79 {
80  message(tr("Aborting site: %1").arg(url.toString()), WadseekerLib::Notice);
81  wadseeker.skipSiteSeek(url);
82 }
83 
84 void WadseekerInterface::accept()
85 {
86  if (isAutomatic())
87  {
88  if (d->bCompletedSuccessfully)
89  {
90  done(QDialog::Accepted);
91  }
92  }
93  else
94  {
95  if (d->leWadName->text().isEmpty())
96  {
97  return;
98  }
99 
100  startSeeking(d->leWadName->text().split(',', QString::SkipEmptyParts));
101  }
102 }
103 
104 void WadseekerInterface::allDone(bool bSuccess)
105 {
106  setStateWaiting();
107  d->bCompletedSuccessfully = bSuccess;
108  QApplication::alert(this);
109  if (bSuccess)
110  {
111  displayMessage(tr("All done. Success."), WadseekerLib::NoticeImportant, false);
112 
113  if (isAutomatic() && !d->preventGame)
114  {
115  if (isActiveWindow())
116  {
117  done(QDialog::Accepted);
118  }
119  else
120  {
121  d->btnStartGame->show();
122  }
123  }
124  }
125  else
126  {
127  QStringList failures = unsuccessfulWads();
128 
129  foreach (const QString& failure, failures)
130  {
131  d->twWads->setFileFailed(failure);
132  }
133 
134  displayMessage(tr("All done. Fail."), WadseekerLib::CriticalError, false);
135  }
136 }
137 
138 void WadseekerInterface::connectWadseekerObject()
139 {
140  // Connect Wadseeker to the dialog box.
141  this->connect(&wadseeker, SIGNAL( allDone(bool) ),
142  SLOT( allDone(bool) ) );
143  this->connect(&wadseeker, SIGNAL( message(const QString&, WadseekerLib::MessageType) ),
144  SLOT( message(const QString&, WadseekerLib::MessageType) ) );
145  this->connect(&wadseeker, SIGNAL( seekStarted(const QStringList&) ),
146  SLOT( seekStarted(const QStringList&) ) );
147  this->connect(&wadseeker, SIGNAL( fileInstalled(const QString&) ),
148  SLOT( fileDownloadSuccessful(const QString&) ) );
149  this->connect(&wadseeker, SIGNAL( siteFinished(const QUrl&) ),
150  SLOT( siteFinished(const QUrl&) ) );
151  this->connect(&wadseeker, SIGNAL( siteProgress(const QUrl&, qint64, qint64) ),
152  SLOT( siteProgress(const QUrl&, qint64, qint64) ) );
153  this->connect(&wadseeker, SIGNAL( siteRedirect(const QUrl&, const QUrl&) ),
154  SLOT( siteRedirect(const QUrl&, const QUrl&) ) );
155  this->connect(&wadseeker, SIGNAL( siteStarted(const QUrl&) ),
156  SLOT( siteStarted(const QUrl&) ) );
157  this->connect(&wadseeker, SIGNAL( serviceStarted(QString) ),
158  SLOT( serviceStarted(QString) ) );
159  this->connect(&wadseeker, SIGNAL( serviceFinished(QString) ),
160  SLOT( serviceFinished(QString) ) );
161 
162  // Connect Wadseeker to the WADs table widget.
163  d->twWads->connect(&wadseeker, SIGNAL( fileDownloadFinished(const QString&) ),
164  SLOT( setFileDownloadFinished(const QString&) ) );
165  d->twWads->connect(&wadseeker, SIGNAL( fileDownloadProgress(const QString&, qint64, qint64) ),
166  SLOT( setFileProgress(const QString&, qint64, qint64) ) );
167  d->twWads->connect(&wadseeker, SIGNAL( fileDownloadStarted(const QString&, const QUrl&) ),
168  SLOT( setFileUrl(const QString&, const QUrl&) ) );
169 }
170 
171 void WadseekerInterface::construct()
172 {
173  d->setupUi(this);
174  d->preventGame = false;
175  d->bCompletedSuccessfully = false;
176 
177  d->taskbarButton = new TaskbarButton(this);
178 
179  d->taskbarProgress = d->taskbarButton->progress();
180  d->taskbarProgress->setMaximum(d->pbOverallProgress->maximum());
181 
182  setStateWaiting();
183 
184  initMessageColors();
185 
186  this->setWindowIcon(QIcon(":/icon.png"));
187  d->btnStartGame->hide();
188  this->connect(&updateTimer, SIGNAL(timeout()), SLOT(registerUpdateRequest()));
189 
190  connectWadseekerObject();
191 
192  // Connect tables.
193  this->connect(d->twWads, SIGNAL( rightMouseClick(const QModelIndex&, const QPoint&) ),
194  SLOT( wadsTableRightClicked(const QModelIndex&, const QPoint&) ) );
195 
196  bAutomatic = false;
197  bFirstShown = false;
198 
199  QStringList urlList = gConfig.wadseeker.searchURLs;
200  if (gConfig.wadseeker.bAlwaysUseDefaultSites)
201  {
202  for (int i = 0; !Wadseeker::defaultSites[i].isEmpty(); ++i)
203  {
204  urlList << Wadseeker::defaultSites[i];
205  }
206  }
207 
208  wadseeker.setPrimarySites(urlList);
209 
210  updateTimer.setSingleShot(false);
211  updateTimer.start(UPDATE_INTERVAL_MS);
212 }
213 
214 WadseekerInterface *WadseekerInterface::create(QWidget* parent)
215 {
216  if (!isInstantiated())
217  {
218  currentInstance = new WadseekerInterface(parent);
219  return currentInstance;
220  }
221  return NULL;
222 }
223 
224 WadseekerInterface *WadseekerInterface::create(ServerPtr server, QWidget* parent)
225 {
226  if (!isInstantiated())
227  {
228  currentInstance = new WadseekerInterface(server, parent);
229  return currentInstance;
230  }
231  return NULL;
232 }
233 
234 WadseekerInterface *WadseekerInterface::createAutoNoGame(QWidget* parent)
235 {
236  WadseekerInterface *interface = create(parent);
237  if (interface != NULL)
238  {
239  interface->setupAutomatic();
240  interface->d->preventGame = true;
241  }
242  return interface;
243 }
244 
245 void WadseekerInterface::displayMessage(const QString& message, WadseekerLib::MessageType type, bool bPrependErrorsWithMessageType)
246 {
247  QString strProcessedMessage;
248 
249  bool bPrependWithNewline = false;
250  QString wrapHtmlLeft = "<div style=\"%1\">";
251  QString wrapHtmlRight = "</div>";
252  QString htmlStyle;
253 
254  switch (type)
255  {
256  case WadseekerLib::CriticalError:
257  htmlStyle = QString("color: %1; font-weight: bold;").arg(colorHtmlMessageFatalError);
258  bPrependWithNewline = true;
259 
260  if (bPrependErrorsWithMessageType)
261  {
262  strProcessedMessage = tr("CRITICAL ERROR: %1").arg(message);
263  }
264  else
265  {
266  strProcessedMessage = message;
267  }
268 
269  setStateWaiting();
270  break;
271 
272  case WadseekerLib::Error:
273  htmlStyle = QString("color: %1;").arg(colorHtmlMessageError);
274 
275  if (bPrependErrorsWithMessageType)
276  {
277  strProcessedMessage = tr("Error: %1").arg(message);
278  }
279  else
280  {
281  strProcessedMessage = message;
282  }
283  break;
284 
285  case WadseekerLib::Notice:
286  htmlStyle = QString("color: %1;").arg(colorHtmlMessageNotice);
287 
288  strProcessedMessage = message;
289  break;
290 
291  case WadseekerLib::NoticeImportant:
292  htmlStyle = QString("color: %1; font-weight: bold;").arg(colorHtmlMessageNotice);
293  bPrependWithNewline = true;
294 
295  strProcessedMessage = message;
296  break;
297  }
298 
299  if (bPrependWithNewline && !d->teWadseekerOutput->toPlainText().isEmpty())
300  {
301  strProcessedMessage = "<br>" + strProcessedMessage;
302  }
303 
304  wrapHtmlLeft = wrapHtmlLeft.arg(htmlStyle);
305 
306  strProcessedMessage = wrapHtmlLeft + strProcessedMessage + wrapHtmlRight;
307 
308  d->teWadseekerOutput->append(strProcessedMessage);
309 }
310 
311 void WadseekerInterface::fileDownloadSuccessful(const QString& filename)
312 {
313  successfulWads << filename;
314  d->twWads->setFileSuccessful(filename);
315 }
316 
317 void WadseekerInterface::initMessageColors()
318 {
319  colorHtmlMessageNotice = gConfig.wadseeker.colorMessageNotice;
320  colorHtmlMessageError = gConfig.wadseeker.colorMessageError;
321  colorHtmlMessageFatalError = gConfig.wadseeker.colorMessageCriticalError;
322 }
323 
324 bool WadseekerInterface::isInstantiated()
325 {
326  return currentInstance != NULL;
327 }
328 
329 void WadseekerInterface::message(const QString& message, WadseekerLib::MessageType type)
330 {
331  displayMessage(message, type, true);
332 }
333 
334 void WadseekerInterface::registerUpdateRequest()
335 {
336  updateProgressBar();
337  updateTitle();
338 }
339 
340 void WadseekerInterface::reject()
341 {
342  switch(state)
343  {
344  case Downloading:
345  wadseeker.abort();
346  break;
347 
348  case Waiting:
349  this->done(Rejected);
350  break;
351  }
352 }
353 
354 void WadseekerInterface::resetTitleToDefault()
355 {
356  setWindowTitle(tr("Wadseeker"));
357 }
358 
359 void WadseekerInterface::seekStarted(const QStringList& filenames)
360 {
361  d->teWadseekerOutput->clear();
362  d->pbOverallProgress->setValue(0);
363  d->taskbarProgress->setValue(0);
364  displayMessage("Seek started on filenames: " + filenames.join(", "), WadseekerLib::Notice, false);
365 
366  seekedWads = filenames;
367  successfulWads.clear();
368  d->twSites->setRowCount(0);
369  d->twWads->setRowCount(0);
370  setStateDownloading();
371 
372  foreach (const QString& name, filenames)
373  {
374  d->twWads->addFile(name);
375  }
376 }
377 
378 void WadseekerInterface::setStateDownloading()
379 {
380  d->btnClose->setText(tr("Abort"));
381  d->btnDownload->setEnabled(false);
382  d->taskbarProgress->show();
383  state = Downloading;
384 }
385 
386 void WadseekerInterface::setStateWaiting()
387 {
388  d->btnClose->setText(tr("Close"));
389  d->btnDownload->setEnabled(true);
390  d->taskbarProgress->hide();
391  state = Waiting;
392 }
393 
394 void WadseekerInterface::setupAutomatic()
395 {
396  bAutomatic = true;
397  d->lblTop->hide();
398  d->btnDownload->hide();
399  d->leWadName->hide();
400 }
401 
402 void WadseekerInterface::setWads(const QStringList& wads)
403 {
404  if (isAutomatic())
405  {
406  seekedWads = wads;
407  }
408  else
409  {
410  d->leWadName->setText(wads.join(", "));
411  }
412 }
413 
414 void WadseekerInterface::setupIdgames()
415 {
416  QString idgamesUrl = Wadseeker::defaultIdgamesUrl();
417  bool useIdgames = true;
418 
419  useIdgames = gConfig.wadseeker.bSearchInIdgames;
420  idgamesUrl = gConfig.wadseeker.idgamesURL;
421 
422  wadseeker.setIdgamesEnabled(useIdgames);
423  wadseeker.setIdgamesUrl(idgamesUrl);
424  wadseeker.setWadArchiveEnabled(gConfig.wadseeker.bSearchInWadArchive);
425 }
426 
427 void WadseekerInterface::showEvent(QShowEvent* event)
428 {
429  if (!bFirstShown)
430  {
431 #if QT_VERSION >= 0x050000
432  d->taskbarButton->setWindow(windowHandle());
433 #endif
434  bFirstShown = true;
435 
436  if (isAutomatic())
437  {
438  startSeeking(seekedWads);
439  }
440  }
441 }
442 
443 void WadseekerInterface::serviceStarted(const QString &service)
444 {
445  d->twSites->addService(service);
446 }
447 
448 void WadseekerInterface::serviceFinished(const QString &service)
449 {
450  d->twSites->removeService(service);
451 }
452 
453 void WadseekerInterface::siteFinished(const QUrl& site)
454 {
455  d->twSites->removeUrl(site);
456  displayMessage("Site finished: " + site.toString(), WadseekerLib::Notice, false);
457 }
458 
459 void WadseekerInterface::siteProgress(const QUrl& site, qint64 bytes, qint64 total)
460 {
461  d->twSites->setUrlProgress(site, bytes, total);
462 }
463 
464 void WadseekerInterface::siteRedirect(const QUrl& oldUrl, const QUrl& newUrl)
465 {
466  d->twSites->removeUrl(oldUrl);
467  d->twSites->addUrl(newUrl);
468  displayMessage("Site redirect: " + oldUrl.toString() + " -> " + newUrl.toString(), WadseekerLib::Notice, false);
469 }
470 
471 void WadseekerInterface::siteStarted(const QUrl& site)
472 {
473  d->twSites->addUrl(site);
474  displayMessage("Site started: " + site.toString(), WadseekerLib::Notice, false);
475 }
476 
477 void WadseekerInterface::startSeeking(const QStringList& seekedFilesList)
478 {
479  if (seekedFilesList.isEmpty())
480  {
481  return;
482  }
483  d->bCompletedSuccessfully = false;
484 
485  // Get rid of the whitespace characters from each filename; we don't want
486  // to be searching " awad.wad".
487  QStringList seekedFilesListFormatted;
488  foreach (QString filenameFormatted, seekedFilesList)
489  {
490  filenameFormatted = filenameFormatted.trimmed();
491 
492  seekedFilesListFormatted << filenameFormatted;
493  }
494 
495  setupIdgames();
496 
497  wadseeker.setTargetDirectory(gConfig.wadseeker.targetDirectory);
498  wadseeker.setCustomSite(customSite);
499  wadseeker.setMaximumConcurrentSeeks(gConfig.wadseeker.maxConcurrentSiteDownloads);
500  wadseeker.setMaximumConcurrentDownloads(gConfig.wadseeker.maxConcurrentWadDownloads);
501  wadseeker.startSeek(seekedFilesListFormatted);
502 }
503 
504 void WadseekerInterface::updateProgressBar()
505 {
506  double totalPercentage = d->twWads->totalDonePercentage();
507  unsigned progressBarValue = (unsigned)(totalPercentage * 100.0);
508 
509  d->pbOverallProgress->setValue(progressBarValue);
510  d->taskbarProgress->setValue(progressBarValue);
511 }
512 
513 void WadseekerInterface::updateTitle()
514 {
515  switch (state)
516  {
517  case Downloading:
518  {
519  double totalPercentage = d->twWads->totalDonePercentage();
520  if (totalPercentage < 0.0)
521  {
522  totalPercentage = 0.0;
523  }
524 
525  setWindowTitle(tr("[%1%] Wadseeker").arg(totalPercentage, 6, 'f', 2));
526  break;
527  }
528 
529  default:
530  case Waiting:
531  resetTitleToDefault();
532  break;
533  }
534 }
535 
536 QStringList WadseekerInterface::unsuccessfulWads() const
537 {
538  QStringList allWads = seekedWads;
539 
540  foreach (const QString& success, successfulWads)
541  {
542  allWads.removeAll(success);
543  }
544 
545  return allWads;
546 }
547 
548 void WadseekerInterface::wadsTableRightClicked(const QModelIndex& index, const QPoint& cursorPosition)
549 {
550  WadseekerWadsTable::ContextMenu* menu = d->twWads->contextMenu(index, cursorPosition);
551 
552  // Disable actions depending on Wadseeker's state.
553  QString fileName = d->twWads->fileNameAtRow(index.row());
554  if (!wadseeker.isDownloadingFile(fileName))
555  {
556  menu->actionSkipCurrentSite->setEnabled(false);
557  }
558 
559  QAction* pResult = menu->exec();
560 
561  if (pResult == menu->actionSkipCurrentSite)
562  {
563  QString wadName = d->twWads->fileNameAtRow(index.row());
564  d->twWads->setFileUrl(fileName, QUrl());
565 
566  wadseeker.skipFileCurrentUrl(wadName);
567  }
568  else if (pResult != NULL)
569  {
570  QMessageBox::warning(this, tr("Context menu error"), tr("Unknown action selected."));
571  }
572 
573  delete menu;
574 }
Platform-agnostic wrapper for QWinTaskbarButton.
Definition: taskbarbutton.h:42
Platform-agnostic wrapper for QWinTaskbarProgress.
void setWads(const QStringList &wads)
Sets WADs to seek.
Wadseeker dialog box, only one instance is allowed.