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