freedoomdialog.cpp
1 //------------------------------------------------------------------------------
2 // freedoomdialog.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library 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 GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; 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) 2015 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "freedoomdialog.h"
24 
25 #include "configuration/doomseekerconfig.h"
26 #include "gui/commongui.h"
27 #include "fileutils.h"
28 #include "pathfinder/filesearchpath.h"
29 #include "pathfinder/pathfinder.h"
30 #include "pathfinder/wadpathfinder.h"
31 #include "ui_freedoomdialog.h"
32 #include <QCheckBox>
33 #include <QCompleter>
34 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
35 #include <QFileSystemModel>
36 #else
37 #include <QDirModel>
38 #endif
39 #include <QMessageBox>
40 #include <wadseeker/entities/checksum.h>
41 #include <wadseeker/entities/modset.h>
42 #include <wadseeker/freedoom.h>
43 #include <wadseeker/modinstall.h>
44 
45 DClass<FreedoomDialog> : public Ui::FreedoomDialog
46 {
47 public:
48  Freedoom *freedoom;
49  ModInstall *modInstall;
50  QString targetDir;
51 };
52 DPointered(FreedoomDialog)
53 
54 FreedoomDialog::FreedoomDialog(QWidget *parent)
55  : QDialog(parent)
56 {
57  d->setupUi(this);
59 
60  d->freedoom = new Freedoom(this);
61  this->connect(d->freedoom, SIGNAL(finished()), SLOT(applyFreedoomVersionInfo()));
62 
63  d->modInstall = new ModInstall(this);
64  this->connect(d->modInstall, SIGNAL(finished()), SLOT(onModInstallFinished()));
65  this->connect(d->modInstall, SIGNAL(fileDownloadProgress(QString,qint64,qint64)),
66  SLOT(showFileDownloadProgress(QString,qint64,qint64)));
67 
68  d->btnInstall->setEnabled(false);
69  setupInstallPaths();
70  setupWadsTable();
71  fetchInfo();
72 }
73 
74 FreedoomDialog::~FreedoomDialog()
75 {
76 }
77 
78 void FreedoomDialog::accept()
79 {
80  ModSet modSet = selectedModFiles();
81  if (modSet.isEmpty())
82  {
83  QMessageBox::critical(this, tr("Install Freedoom"),
84  tr("Select at least one file."));
85  return;
86  }
87  resetProgressBar();
88  showStatus(tr("Downloading & installing ..."));
89  d->targetDir = d->cboInstallPath->currentText();
90  d->modInstall->install(d->targetDir, modSet);
91 }
92 
93 void FreedoomDialog::onModInstallFinished()
94 {
95  if (!d->modInstall->isError())
96  {
97  updateConfig();
98  fetchInfo();
99  }
100  else
101  showError(d->modInstall->error());
102 }
103 
104 void FreedoomDialog::updateConfig()
105 {
106  gConfig.doomseeker.enableFreedoomInstallation(d->targetDir);
107  gConfig.saveToFile();
108 }
109 
110 void FreedoomDialog::fetchInfo()
111 {
112  resetProgressBar();
113  d->btnRetry->hide();
114  showStatus(tr("Downloading Freedoom version info ..."));
115  d->freedoom->requestModSet();
116 }
117 
118 void FreedoomDialog::resetProgressBar()
119 {
120  d->progressBar->setFormat(tr("Working ..."));
121  d->progressBar->show();
122  d->progressBar->setMaximum(0);
123  d->progressBar->setValue(0);
124 }
125 
126 void FreedoomDialog::applyFreedoomVersionInfo()
127 {
128  if (!d->freedoom->isError())
129  showModInfo(d->freedoom->modSet());
130  else
131  showError(tr("Error: %1").arg(d->freedoom->error()));
132 }
133 
134 void FreedoomDialog::showModInfo(const ModSet &modSet)
135 {
136  d->wadsArea->show();
137  d->workInProgressArea->hide();
138 
139  d->wadsTable->setSortingEnabled(false);
140  d->wadsTable->clearContents();
141  while (d->wadsTable->rowCount() > 0)
142  {
143  d->wadsTable->removeRow(d->wadsTable->rowCount() - 1);
144  }
145  for (const ModFile &file : modSet.modFiles())
146  {
147  insertModFile(file);
148  }
149  d->wadsTable->setSortingEnabled(true);
150  d->btnInstall->setEnabled(true);
151 }
152 
153 void FreedoomDialog::insertModFile(const ModFile &file)
154 {
155  WadPathFinder pathFinder = WadPathFinder(PathFinder());
156  pathFinder.setAllowAliases(false);
157  QString location = pathFinder.find(file.fileName()).path();
158 
159  QString status = tr("OK");
160  bool needsInstall = false;
161  if (location.isEmpty())
162  {
163  needsInstall = true;
164  status = tr("Missing");
165  }
166  else
167  {
168  QByteArray md5 = FileUtils::md5(location);
169  for (Checksum checksum : file.checksums())
170  {
171  if (md5 != checksum.hash() && checksum.algorithm() == QCryptographicHash::Md5)
172  {
173  status = tr("Different");
174  needsInstall = true;
175  break;
176  }
177  }
178  }
179  if (file.fileName().compare("freedm.wad", Qt::CaseInsensitive) == 0)
180  needsInstall = false;
181 
182  d->wadsTable->insertRow(d->wadsTable->rowCount());
183  int row = d->wadsTable->rowCount() - 1;
184  d->wadsTable->setItem(row, ColName, new QTableWidgetItem(file.fileName()));
185  d->wadsTable->setItem(row, ColStatus, new QTableWidgetItem(status));
186 
187  auto checkBox = new QCheckBox();
188  checkBox->setChecked(needsInstall);
189  d->wadsTable->setCellWidget(row, ColInstall, checkBox);
190 
191  QString tooltip = tr("<p>File: %1<br>Version: %2<br>"
192  "Description: %3<br>Location: %4</p>")
193  .arg(file.name(), file.version(), file.description(), location);
194 
195  for (int col = 0; col < d->wadsTable->columnCount(); ++col)
196  {
197  QTableWidgetItem *item = d->wadsTable->item(row, col);
198  if (item != nullptr)
199  item->setToolTip(tooltip);
200  }
201 }
202 
203 void FreedoomDialog::showError(const QString &text)
204 {
205  d->btnRetry->setVisible(true);
206  showStatus(text);
207  d->progressBar->hide();
208 }
209 
210 void FreedoomDialog::showStatus(const QString &text)
211 {
212  d->wadsArea->hide();
213  d->workInProgressArea->show();
214  d->btnInstall->setEnabled(false);
215  d->lblWorkInProgress->setText(text);
216 }
217 
218 ModSet FreedoomDialog::selectedModFiles() const
219 {
220  ModSet modSet;
221  for (int row = 0; row < d->wadsTable->rowCount(); ++row)
222  {
223  auto checkbox = static_cast<QCheckBox *>(d->wadsTable->cellWidget(row, ColInstall));
224  if (checkbox->isChecked())
225  {
226  QString fileName = d->wadsTable->item(row, ColName)->text();
227  modSet.addModFile(d->freedoom->modSet().findFileName(fileName));
228  }
229  }
230  return modSet;
231 }
232 
233 void FreedoomDialog::setupInstallPaths()
234 {
235  auto completer = new QCompleter(this);
236 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
237  // NOTE: Although QFileSystemModel is available since Qt 4, there were some
238  // issues with it and QCompleter until at least Qt 5.12 (i.e. QTBUG-38014).
239  // We'll conservatively use QFileSystemModel on Qt 6 only.
240  auto model = new QFileSystemModel(completer);
241  model->setRootPath(gConfig.wadseeker.targetDirectory);
242  completer->setModel(model);
243 #else
244  completer->setModel(new QDirModel(completer));
245 #endif
246  d->cboInstallPath->setCompleter(completer);
247 
248  for (const FileSearchPath &path : gConfig.combinedWadseekPaths())
249  {
250  d->cboInstallPath->addItem(path.path());
251  }
252  d->cboInstallPath->setCurrentText(gConfig.wadseeker.targetDirectory);
253 }
254 
255 void FreedoomDialog::setupWadsTable()
256 {
257  QHeaderView *header = d->wadsTable->horizontalHeader();
258  header->setSectionResizeMode(ColName, QHeaderView::Stretch);
259  header->setSectionResizeMode(ColStatus, QHeaderView::ResizeToContents);
260  header->setSectionResizeMode(ColInstall, QHeaderView::ResizeToContents);
261  d->wadsTable->setColumnWidth(ColName, 150);
262 }
263 
264 void FreedoomDialog::showFileDownloadProgress(const QString &file,
265  qint64 current, qint64 total)
266 {
267  d->progressBar->setFormat(tr("%1 %p%").arg(file));
268  d->progressBar->setMaximum(total);
269  d->progressBar->setValue(current);
270 }