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 }