wadseekerwadstable.cpp
1 //------------------------------------------------------------------------------
2 // wadseekerwadstable.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) 2011 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "wadseekerwadstable.h"
24 
25 #include "speedcalculator.h"
26 #include "strings.hpp"
27 #include "wadseeker/entities/modfile.h"
28 
29 #include <QDebug>
30 #include <QHeaderView>
31 #include <QProgressBar>
32 #include <QUrl>
33 
34 WadseekerWadsTable::WadseekerWadsTable(QWidget *pParent)
35  : TableWidgetMouseAware(pParent)
36 {
37  d.bAlreadyShownOnce = false;
38  d.updateClock.start();
39 }
40 
41 WadseekerWadsTable::~WadseekerWadsTable()
42 {
43  QMap<QString, SpeedCalculator *>::iterator it;
44  for (it = d.speedCalculators.begin(); it != d.speedCalculators.end(); ++it)
45  delete it.value();
46 }
47 
48 void WadseekerWadsTable::addFile(const QString &filename)
49 {
50  // Add new row to table, but only if file is not yet added.
51  if (findFileRow(filename) < 0)
52  {
53  insertRow(rowCount());
54  int rowIndex = rowCount() - 1;
55 
56  // Create the row contents.
57  setSortingEnabled(false);
58 
59  auto pBar = new QProgressBar();
60  pBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
61  pBar->setFixedWidth(PROGRESS_COLUMN_WIDTH);
62  pBar->setAlignment(Qt::AlignCenter);
63 
64  setItem(rowIndex, IDX_NAME_COLUMN, new QTableWidgetItem(filename));
65  setItem(rowIndex, IDX_URL_COLUMN, new QTableWidgetItem());
66  setCellWidget(rowIndex, IDX_PROGRESS_COLUMN, pBar);
67  setItem(rowIndex, IDX_SPEED_COLUMN, new QTableWidgetItem());
68  setItem(rowIndex, IDX_ETA_COLUMN, new QTableWidgetItem(tr("N/A")));
69  setItem(rowIndex, IDX_SIZE_COLUMN, new QTableWidgetItem(tr("N/A")));
70 
71  auto pCalculator = new SpeedCalculator();
72  pCalculator->start();
73 
74  d.speedCalculators.insert(filename, pCalculator);
75 
76  setSortingEnabled(true);
77  }
78 }
79 
80 WadseekerWadsTable::ContextMenu *WadseekerWadsTable::contextMenu(const QModelIndex &index, const QPoint &cursorPosition)
81 {
82  auto menu = new ContextMenu(this);
83  QPoint displayPoint = this->viewport()->mapToGlobal(cursorPosition);
84  menu->move(displayPoint);
85 
86  if (!index.isValid())
87  menu->actionSkipCurrentSite->setEnabled(false);
88 
89  return menu;
90 }
91 
93 {
94  if (row < 0 || row >= this->rowCount())
95  return -1;
96 
97  QString fileName = fileNameAtRow(row);
98  return d.speedCalculators[fileName]->expectedDataSize();
99 }
100 
101 QString WadseekerWadsTable::fileNameAtRow(int row) const
102 {
103  if (row < 0 || row >= this->rowCount())
104  return QString();
105 
106  return item(row, IDX_NAME_COLUMN)->text();
107 }
108 
109 int WadseekerWadsTable::findFileRow(const QString &filename)
110 {
111  if (filename.isEmpty())
112  return -1;
113 
114  QList<QTableWidgetItem *> list = findItems(filename, Qt::MatchFixedString);
115  if (!list.isEmpty())
116  return list.first()->row();
117 
118  return -1;
119 }
120 
121 void WadseekerWadsTable::setFileDownloadFinished(const ModFile &filename)
122 {
123  int row = findFileRow(filename.fileName());
124 
125  if (row >= 0)
126  {
127  // Update ETA
128  // ETA will be changed to DONE if wad is installed successfully.
129  item(row, IDX_ETA_COLUMN)->setText(tr("N/A"));
130  item(row, IDX_SPEED_COLUMN)->setText(tr("N/A"));
131 
132  item(row, IDX_URL_COLUMN)->setText(tr("Awaiting URLs"));
133  item(row, IDX_URL_COLUMN)->setToolTip(tr("Awaiting URLs"));
134 
135  const bool FORCE = true;
136  updateDataInfoValues(FORCE);
137  }
138 }
139 
140 void WadseekerWadsTable::setFileFailed(const ModFile &filename)
141 {
142  int row = findFileRow(filename.fileName());
143 
144  if (row >= 0)
145  {
146  item(row, IDX_NAME_COLUMN)->setIcon(QIcon(":/icons/x.png"));
147 
148  item(row, IDX_URL_COLUMN)->setText("");
149  }
150 }
151 
152 void WadseekerWadsTable::setFileProgress(const ModFile &filename, qint64 current, qint64 total)
153 {
154  int row = findFileRow(filename.fileName());
155 
156  if (row >= 0)
157  {
158  auto pBar = (QProgressBar *) this->cellWidget(row, IDX_PROGRESS_COLUMN);
159  pBar->setMaximum(total);
160  pBar->setValue(current);
161 
162  // Update ETA and speed
163  SpeedCalculator *pCalculator = d.speedCalculators.value(filename.fileName());
164  pCalculator->setExpectedDataSize(total);
165  pCalculator->registerDataAmount(current);
166 
167  const bool FORCE = true;
168  updateDataInfoValues(!FORCE);
169  }
170 }
171 
172 void WadseekerWadsTable::setFileSuccessful(const ModFile &filename)
173 {
174  int row = findFileRow(filename.fileName());
175 
176  if (row >= 0)
177  {
178  // Set progress bar to 100%.
179  auto pBar = (QProgressBar *) this->cellWidget(row, IDX_PROGRESS_COLUMN);
180  SpeedCalculator *pCalculator = d.speedCalculators[filename.fileName()];
181  if (pCalculator->expectedDataSize() == 0)
182  pCalculator->setExpectedDataSize(1);
183 
184  pCalculator->registerDataAmount(pCalculator->expectedDataSize());
185 
186  pBar->setMaximum(pCalculator->expectedDataSize());
187  pBar->setValue(pCalculator->expectedDataSize());
188 
189  item(row, IDX_NAME_COLUMN)->setIcon(QIcon(":/icons/ok.png"));
190  item(row, IDX_URL_COLUMN)->setText("");
191 
192  item(row, IDX_ETA_COLUMN)->setText(tr("Done"));
193  item(row, IDX_SPEED_COLUMN)->setText("");
194 
195  const bool FORCE = true;
196  updateDataInfoValues(FORCE);
197  }
198 }
199 
200 void WadseekerWadsTable::setFileUrl(const ModFile &filename, const QUrl &url)
201 {
202  // At this point we know that a new download has started.
203  // We should reset certain values.
204  int row = findFileRow(filename.fileName());
205 
206  if (row >= 0)
207  {
208  QTableWidgetItem *pItem = this->item(row, IDX_URL_COLUMN);
209  pItem->setText(url.toString());
210  pItem->setToolTip(url.toString());
211 
212  auto pBar = (QProgressBar *) this->cellWidget(row, IDX_PROGRESS_COLUMN);
213  pBar->setMaximum(0);
214  pBar->setValue(0);
215 
216  SpeedCalculator *pCalculator = d.speedCalculators[filename.fileName()];
217  pCalculator->start();
218  }
219 }
220 
221 void WadseekerWadsTable::showEvent(QShowEvent *pEvent)
222 {
223  Q_UNUSED(pEvent);
224  if (!d.bAlreadyShownOnce)
225  {
226  // Events in this block must occur after the widget has been
227  // constructed, but only once.
228  QHeaderView *pHeader = horizontalHeader();
229 
230  // Setup resizing
231  pHeader->setSectionResizeMode(IDX_URL_COLUMN, QHeaderView::Stretch);
232  pHeader->setSectionResizeMode(IDX_PROGRESS_COLUMN, QHeaderView::Fixed);
233 
234  pHeader->resizeSection(IDX_NAME_COLUMN, 140);
235  pHeader->resizeSection(IDX_PROGRESS_COLUMN, PROGRESS_COLUMN_WIDTH);
236  pHeader->resizeSection(IDX_ETA_COLUMN, 85);
237  pHeader->resizeSection(IDX_SIZE_COLUMN, 150);
238  pHeader->resizeSection(IDX_SPEED_COLUMN, 85);
239  }
240 }
241 
243 {
244  double sumDownloadPercentages = 0.0;
245 
246  if (this->rowCount() == 0)
247  return -1.0;
248 
249  for (int i = 0; i < this->rowCount(); ++i)
250  {
251  const auto pBar = (const QProgressBar *) this->cellWidget(i, IDX_PROGRESS_COLUMN);
252  if (pBar != nullptr)
253  {
254  int val = pBar->value();
255  int max = pBar->maximum();
256 
257  if (max > 0)
258  {
259  double curPercentage = (double) val / (double) max;
260  sumDownloadPercentages += curPercentage;
261  }
262  }
263  }
264 
265  // We need to multiply the value by 100 to get actual percents.
266  sumDownloadPercentages *= 100.0;
267  double averageDownloadPercentages = sumDownloadPercentages / (double) this->rowCount();
268 
269  return averageDownloadPercentages;
270 }
271 
272 void WadseekerWadsTable::updateDataInfoValues(bool bForce)
273 {
274  // Make sure updates are not performed before certain interval passes.
275  if (d.updateClock.elapsed() > UPDATE_INTERVAL_MS || bForce)
276  {
277  d.updateClock.start();
278 
279  for (int i = 0; i < this->rowCount(); ++i)
280  {
281  // Find the calculator for specified row.
282  QString filename = this->item(i, IDX_NAME_COLUMN)->text();
283  SpeedCalculator *pCalculator = d.speedCalculators.value(filename);
284 
285  // Update data amount.
286  QString strCurrent = Strings::formatDataAmount(pCalculator->lastRegisterAttemptedDataAmount());
287  QString strTotal = Strings::formatDataAmount(pCalculator->expectedDataSize());
288 
289  QString size = QString("%1 / %2").arg(strCurrent, strTotal);
290  item(i, IDX_SIZE_COLUMN)->setText(size);
291 
292  // Update ETA and speed.
293  if (pCalculator->expectedDataSize() != pCalculator->lastRegisterAttemptedDataAmount())
294  {
295  // If both above values are equal it means we have either
296  // finished the download or didn't start it yet. In either case
297  // we shouldn't change the speed and ETA displays.
298  long double ldEta = pCalculator->estimatedTimeUntilArrival();
299  long double ldSpeed = pCalculator->getSpeed();
300 
301  if (ldEta >= 0.0)
302  {
303  QString strEta = Strings::formatTime(pCalculator->estimatedTimeUntilArrival());
304  item(i, IDX_ETA_COLUMN)->setText(strEta);
305  }
306  else
307  item(i, IDX_ETA_COLUMN)->setText(tr("N/A"));
308 
309  if (ldSpeed >= 0.0)
310  {
311  QString strSpeed = Strings::formatDataSpeed(pCalculator->getSpeed());
312  item(i, IDX_SPEED_COLUMN)->setText(strSpeed);
313  }
314  else
315  item(i, IDX_SPEED_COLUMN)->setText(tr("N/A"));
316  }
317  }
318  }
319 }
321 WadseekerWadsTable::ContextMenu::ContextMenu(QWidget *pParent)
322  : QMenu(pParent)
323 {
324  this->actionSkipCurrentSite = new QAction(tr("Skip current URL"), this);
325 
326  this->addAction(this->actionSkipCurrentSite);
327 }