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  QList<QTableWidgetItem *> list = findItems(filename, Qt::MatchFixedString);
112  if (!list.isEmpty())
113  return list.first()->row();
114 
115  return -1;
116 }
117 
118 void WadseekerWadsTable::setFileDownloadFinished(const ModFile &filename)
119 {
120  int row = findFileRow(filename.fileName());
121 
122  if (row >= 0)
123  {
124  // Update ETA
125  // ETA will be changed to DONE if wad is installed successfully.
126  item(row, IDX_ETA_COLUMN)->setText(tr("N/A"));
127  item(row, IDX_SPEED_COLUMN)->setText(tr("N/A"));
128 
129  item(row, IDX_URL_COLUMN)->setText(tr("Awaiting URLs"));
130  item(row, IDX_URL_COLUMN)->setToolTip(tr("Awaiting URLs"));
131 
132  const bool FORCE = true;
133  updateDataInfoValues(FORCE);
134  }
135 }
136 
137 void WadseekerWadsTable::setFileFailed(const ModFile &filename)
138 {
139  int row = findFileRow(filename.fileName());
140 
141  if (row >= 0)
142  {
143  item(row, IDX_NAME_COLUMN)->setIcon(QIcon(":/icons/x.png"));
144 
145  item(row, IDX_URL_COLUMN)->setText("");
146  }
147 }
148 
149 void WadseekerWadsTable::setFileProgress(const ModFile &filename, qint64 current, qint64 total)
150 {
151  int row = findFileRow(filename.fileName());
152 
153  if (row >= 0)
154  {
155  auto pBar = (QProgressBar *) this->cellWidget(row, IDX_PROGRESS_COLUMN);
156  pBar->setMaximum(total);
157  pBar->setValue(current);
158 
159  // Update ETA and speed
160  SpeedCalculator *pCalculator = d.speedCalculators.value(filename.fileName());
161  pCalculator->setExpectedDataSize(total);
162  pCalculator->registerDataAmount(current);
163 
164  const bool FORCE = true;
165  updateDataInfoValues(!FORCE);
166  }
167 }
168 
169 void WadseekerWadsTable::setFileSuccessful(const ModFile &filename)
170 {
171  int row = findFileRow(filename.fileName());
172 
173  if (row >= 0)
174  {
175  // Set progress bar to 100%.
176  auto pBar = (QProgressBar *) this->cellWidget(row, IDX_PROGRESS_COLUMN);
177  SpeedCalculator *pCalculator = d.speedCalculators[filename.fileName()];
178  if (pCalculator->expectedDataSize() == 0)
179  pCalculator->setExpectedDataSize(1);
180 
181  pCalculator->registerDataAmount(pCalculator->expectedDataSize());
182 
183  pBar->setMaximum(pCalculator->expectedDataSize());
184  pBar->setValue(pCalculator->expectedDataSize());
185 
186  item(row, IDX_NAME_COLUMN)->setIcon(QIcon(":/icons/ok.png"));
187  item(row, IDX_URL_COLUMN)->setText("");
188 
189  item(row, IDX_ETA_COLUMN)->setText(tr("Done"));
190  item(row, IDX_SPEED_COLUMN)->setText("");
191 
192  const bool FORCE = true;
193  updateDataInfoValues(FORCE);
194  }
195 }
196 
197 void WadseekerWadsTable::setFileUrl(const ModFile &filename, const QUrl &url)
198 {
199  // At this point we know that a new download has started.
200  // We should reset certain values.
201  int row = findFileRow(filename.fileName());
202 
203  if (row >= 0)
204  {
205  QTableWidgetItem *pItem = this->item(row, IDX_URL_COLUMN);
206  pItem->setText(url.toString());
207  pItem->setToolTip(url.toString());
208 
209  auto pBar = (QProgressBar *) this->cellWidget(row, IDX_PROGRESS_COLUMN);
210  pBar->setMaximum(0);
211  pBar->setValue(0);
212 
213  SpeedCalculator *pCalculator = d.speedCalculators[filename.fileName()];
214  pCalculator->start();
215  }
216 }
217 
218 void WadseekerWadsTable::showEvent(QShowEvent *pEvent)
219 {
220  Q_UNUSED(pEvent);
221  if (!d.bAlreadyShownOnce)
222  {
223  // Events in this block must occur after the widget has been
224  // constructed, but only once.
225  QHeaderView *pHeader = horizontalHeader();
226 
227  // Setup resizing
228  pHeader->setSectionResizeMode(IDX_URL_COLUMN, QHeaderView::Stretch);
229  pHeader->setSectionResizeMode(IDX_PROGRESS_COLUMN, QHeaderView::Fixed);
230 
231  pHeader->resizeSection(IDX_NAME_COLUMN, 140);
232  pHeader->resizeSection(IDX_PROGRESS_COLUMN, PROGRESS_COLUMN_WIDTH);
233  pHeader->resizeSection(IDX_ETA_COLUMN, 85);
234  pHeader->resizeSection(IDX_SIZE_COLUMN, 150);
235  pHeader->resizeSection(IDX_SPEED_COLUMN, 85);
236  }
237 }
238 
240 {
241  double sumDownloadPercentages = 0.0;
242 
243  if (this->rowCount() == 0)
244  return -1.0;
245 
246  for (int i = 0; i < this->rowCount(); ++i)
247  {
248  const auto pBar = (const QProgressBar *) this->cellWidget(i, IDX_PROGRESS_COLUMN);
249  if (pBar != nullptr)
250  {
251  int val = pBar->value();
252  int max = pBar->maximum();
253 
254  if (max > 0)
255  {
256  double curPercentage = (double) val / (double) max;
257  sumDownloadPercentages += curPercentage;
258  }
259  }
260  }
261 
262  // We need to multiply the value by 100 to get actual percents.
263  sumDownloadPercentages *= 100.0;
264  double averageDownloadPercentages = sumDownloadPercentages / (double) this->rowCount();
265 
266  return averageDownloadPercentages;
267 }
268 
269 void WadseekerWadsTable::updateDataInfoValues(bool bForce)
270 {
271  // Make sure updates are not performed before certain interval passes.
272  if (d.updateClock.elapsed() > UPDATE_INTERVAL_MS || bForce)
273  {
274  d.updateClock.start();
275 
276  for (int i = 0; i < this->rowCount(); ++i)
277  {
278  // Find the calculator for specified row.
279  QString filename = this->item(i, IDX_NAME_COLUMN)->text();
280  SpeedCalculator *pCalculator = d.speedCalculators.value(filename);
281 
282  // Update data amount.
283  QString strCurrent = Strings::formatDataAmount(pCalculator->lastRegisterAttemptedDataAmount());
284  QString strTotal = Strings::formatDataAmount(pCalculator->expectedDataSize());
285 
286  QString size = QString("%1 / %2").arg(strCurrent, strTotal);
287  item(i, IDX_SIZE_COLUMN)->setText(size);
288 
289  // Update ETA and speed.
290  if (pCalculator->expectedDataSize() != pCalculator->lastRegisterAttemptedDataAmount())
291  {
292  // If both above values are equal it means we have either
293  // finished the download or didn't start it yet. In either case
294  // we shouldn't change the speed and ETA displays.
295  long double ldEta = pCalculator->estimatedTimeUntilArrival();
296  long double ldSpeed = pCalculator->getSpeed();
297 
298  if (ldEta >= 0.0)
299  {
300  QString strEta = Strings::formatTime(pCalculator->estimatedTimeUntilArrival());
301  item(i, IDX_ETA_COLUMN)->setText(strEta);
302  }
303  else
304  item(i, IDX_ETA_COLUMN)->setText(tr("N/A"));
305 
306  if (ldSpeed >= 0.0)
307  {
308  QString strSpeed = Strings::formatDataSpeed(pCalculator->getSpeed());
309  item(i, IDX_SPEED_COLUMN)->setText(strSpeed);
310  }
311  else
312  item(i, IDX_SPEED_COLUMN)->setText(tr("N/A"));
313  }
314  }
315  }
316 }
318 WadseekerWadsTable::ContextMenu::ContextMenu(QWidget *pParent)
319  : QMenu(pParent)
320 {
321  this->actionSkipCurrentSite = new QAction(tr("Skip current URL"), this);
322 
323  this->addAction(this->actionSkipCurrentSite);
324 }