qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
filetransferwidget.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2014-2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "filetransferwidget.h"
21 #include "ui_filetransferwidget.h"
22 
23 #include "src/core/corefile.h"
25 #include "src/widget/gui.h"
26 #include "src/widget/style.h"
27 #include "src/widget/widget.h"
29 #include "util/display.h"
30 
31 #include <QBuffer>
32 #include <QDebug>
33 #include <QDesktopServices>
34 #include <QDesktopWidget>
35 #include <QFile>
36 #include <QFileDialog>
37 #include <QMessageBox>
38 #include <QMouseEvent>
39 #include <QPainter>
40 #include <QVariantAnimation>
41 
42 #include <cassert>
43 #include <math.h>
44 
45 
46 // The leftButton is used to accept, pause, or resume a file transfer, as well as to open a
47 // received file.
48 // The rightButton is used to cancel a file transfer, or to open the directory a file was
49 // downloaded to.
50 
51 FileTransferWidget::FileTransferWidget(QWidget* parent, CoreFile& _coreFile, ToxFile file)
52  : QWidget(parent)
53  , coreFile{_coreFile}
54  , ui(new Ui::FileTransferWidget)
55  , fileInfo(file)
56  , backgroundColor(Style::getColor(Style::TransferMiddle))
57  , buttonColor(Style::getColor(Style::TransferWait))
58  , buttonBackgroundColor(Style::getColor(Style::GroundBase))
59  , active(true)
60 {
61  ui->setupUi(this);
62 
63  // hide the QWidget background (background-color: transparent doesn't seem to work)
64  setAttribute(Qt::WA_TranslucentBackground, true);
65 
66  ui->previewButton->hide();
67  ui->filenameLabel->setText(file.fileName);
68  ui->progressBar->setValue(0);
69  ui->fileSizeLabel->setText(getHumanReadableSize(file.progress.getFileSize()));
70  ui->etaLabel->setText("");
71 
72  backgroundColorAnimation = new QVariantAnimation(this);
73  backgroundColorAnimation->setDuration(500);
74  backgroundColorAnimation->setEasingCurve(QEasingCurve::OutCubic);
75  connect(backgroundColorAnimation, &QVariantAnimation::valueChanged, this,
76  [this](const QVariant& val) {
77  backgroundColor = val.value<QColor>();
78  update();
79  });
80 
81  buttonColorAnimation = new QVariantAnimation(this);
82  buttonColorAnimation->setDuration(500);
83  buttonColorAnimation->setEasingCurve(QEasingCurve::OutCubic);
84  connect(buttonColorAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& val) {
85  buttonColor = val.value<QColor>();
86  update();
87  });
88 
89  connect(ui->leftButton, &QPushButton::clicked, this, &FileTransferWidget::onLeftButtonClicked);
90  connect(ui->rightButton, &QPushButton::clicked, this, &FileTransferWidget::onRightButtonClicked);
91  connect(ui->previewButton, &QPushButton::clicked, this,
93 
95 
96  // Set lastStatus to anything but the file's current value, this forces an update
98  updateWidget(file);
99 
100  setFixedHeight(64);
101 }
102 
104 {
105  delete ui;
106 }
107 
108 // TODO(sudden6): remove file IO from the UI
114 bool FileTransferWidget::tryRemoveFile(const QString& filepath)
115 {
116  QFile tmp(filepath);
117  bool writable = tmp.open(QIODevice::WriteOnly);
118  tmp.remove();
119  return writable;
120 }
121 
123 {
125 }
126 
128 {
129  return active;
130 }
131 
132 void FileTransferWidget::acceptTransfer(const QString& filepath)
133 {
134  if (filepath.isEmpty()) {
135  return;
136  }
137 
138  // test if writable
139  if (!tryRemoveFile(filepath)) {
140  GUI::showWarning(tr("Location not writable", "Title of permissions popup"),
141  tr("You do not have permission to write that location. Choose another, or "
142  "cancel the save dialog.",
143  "text of permissions popup"));
144  return;
145  }
146 
147  // everything ok!
149 }
150 
151 void FileTransferWidget::setBackgroundColor(const QColor& c, bool whiteFont)
152 {
153  if (c != backgroundColor) {
155  backgroundColorAnimation->setEndValue(c);
156  backgroundColorAnimation->start();
157  }
158 
159  setProperty("fontColor", whiteFont ? "white" : "black");
160 
161  setStyleSheet(Style::getStylesheet("fileTransferInstance/filetransferWidget.css"));
162  Style::repolish(this);
163 
164  update();
165 }
166 
168 {
169  if (c != buttonColor) {
170  buttonColorAnimation->setStartValue(buttonColor);
171  buttonColorAnimation->setEndValue(c);
172  buttonColorAnimation->start();
173  }
174 }
175 
177 {
178  return (ui->rightButton->isVisible() || ui->leftButton->isVisible())
179  && !(ui->leftButton->isVisible() && ui->leftButton->objectName() == "ok");
180 }
181 
183 {
184  // required by Hi-DPI support as border-image doesn't work.
185  QPainter painter(this);
186  painter.setRenderHint(QPainter::Antialiasing);
187  painter.setPen(Qt::NoPen);
188 
189  qreal ratio = static_cast<qreal>(geometry().height()) / static_cast<qreal>(geometry().width());
190  const int r = 24;
191  const int buttonFieldWidth = 32;
192  const int lineWidth = 1;
193 
194  // Draw the widget background:
195  painter.setClipRect(QRect(0, 0, width(), height()));
196  painter.setBrush(QBrush(backgroundColor));
197  painter.drawRoundedRect(geometry(), r * ratio, r, Qt::RelativeSize);
198 
199  if (drawButtonAreaNeeded()) {
200  // Draw the button background:
201  QPainterPath buttonBackground;
202  buttonBackground.addRoundedRect(width() - 2 * buttonFieldWidth - lineWidth * 2, 0,
203  buttonFieldWidth, buttonFieldWidth + lineWidth, 50, 50,
204  Qt::RelativeSize);
205  buttonBackground.addRect(width() - 2 * buttonFieldWidth - lineWidth * 2, 0,
206  buttonFieldWidth * 2, buttonFieldWidth / 2);
207  buttonBackground.addRect(width() - 1.5 * buttonFieldWidth - lineWidth * 2, 0,
208  buttonFieldWidth * 2, buttonFieldWidth + 1);
209  buttonBackground.setFillRule(Qt::WindingFill);
210  painter.setBrush(QBrush(buttonBackgroundColor));
211  painter.drawPath(buttonBackground);
212 
213  // Draw the left button:
214  QPainterPath leftButton;
215  leftButton.addRoundedRect(QRect(width() - 2 * buttonFieldWidth - lineWidth, 0,
216  buttonFieldWidth, buttonFieldWidth),
217  50, 50, Qt::RelativeSize);
218  leftButton.addRect(QRect(width() - 2 * buttonFieldWidth - lineWidth, 0,
219  buttonFieldWidth / 2, buttonFieldWidth / 2));
220  leftButton.addRect(QRect(width() - 1.5 * buttonFieldWidth - lineWidth, 0,
221  buttonFieldWidth / 2, buttonFieldWidth));
222  leftButton.setFillRule(Qt::WindingFill);
223  painter.setBrush(QBrush(buttonColor));
224  painter.drawPath(leftButton);
225 
226  // Draw the right button:
227  painter.setBrush(QBrush(buttonColor));
228  painter.setClipRect(QRect(width() - buttonFieldWidth, 0, buttonFieldWidth, buttonFieldWidth));
229  painter.drawRoundedRect(geometry(), r * ratio, r, Qt::RelativeSize);
230  }
231 }
232 
234 {
236 }
237 
239 {
240  if (lastStatus == file.status) {
241  return;
242  }
243 
244  updateBackgroundColor(file.status);
245 }
246 
248 {
249  if (lastStatus == file.status && file.status != ToxFile::PAUSED) {
250  return;
251  }
252 
253  switch (file.status) {
255  if (file.direction == ToxFile::SENDING) {
256  ui->progressLabel->setText(tr("Waiting to send...", "file transfer widget"));
257  } else {
258  ui->progressLabel->setText(tr("Accept to receive this file", "file transfer widget"));
259  }
260  break;
261  case ToxFile::PAUSED:
262  ui->etaLabel->setText("");
263  if (file.pauseStatus.localPaused()) {
264  ui->progressLabel->setText(tr("Paused", "file transfer widget"));
265  } else {
266  ui->progressLabel->setText(tr("Remote paused", "file transfer widget"));
267  }
268  break;
270  ui->etaLabel->setText("");
271  ui->progressLabel->setText(tr("Resuming...", "file transfer widget"));
272  break;
273  case ToxFile::BROKEN:
274  case ToxFile::CANCELED:
275  break;
276  case ToxFile::FINISHED:
277  break;
278  default:
279  qCritical() << "Invalid file status";
280  assert(false);
281  }
282 }
283 
285 {
286  if (lastStatus == file.status) {
287  return;
288  }
289 
290  switch (file.status) {
292  case ToxFile::PAUSED:
294  case ToxFile::BROKEN:
295  case ToxFile::CANCELED:
296  if (file.direction == ToxFile::SENDING) {
297  showPreview(file.filePath);
298  }
299  break;
300  case ToxFile::FINISHED:
301  showPreview(file.filePath);
302  break;
303  default:
304  qCritical() << "Invalid file status";
305  assert(false);
306  }
307 }
308 
310 {
311  switch (file.status) {
313  case ToxFile::PAUSED:
314  break;
315  case ToxFile::TRANSMITTING: {
316  auto speed = file.progress.getSpeed();
317  auto progress = file.progress.getProgress();
318  auto remainingTime = file.progress.getTimeLeftSeconds();
319 
320  ui->progressBar->setValue(static_cast<int>(progress * 100.0));
321 
322  // update UI
323  if (speed > 0) {
324  // ETA
325  QTime toGo = QTime(0, 0).addSecs(remainingTime);
326  QString format = toGo.hour() > 0 ? "hh:mm:ss" : "mm:ss";
327  ui->etaLabel->setText(toGo.toString(format));
328  } else {
329  ui->etaLabel->setText("");
330  }
331 
332  ui->progressLabel->setText(getHumanReadableSize(speed) + "/s");
333  break;
334  }
335  case ToxFile::BROKEN:
336  case ToxFile::CANCELED:
337  case ToxFile::FINISHED: {
338  ui->progressBar->hide();
339  ui->progressLabel->hide();
340  ui->etaLabel->hide();
341  break;
342  }
343  default:
344  qCritical() << "Invalid file status";
345  assert(false);
346  }
347 }
348 
350 {
351  if (lastStatus == file.status) {
352  return;
353  }
354 
355  switch (file.status) {
356  case ToxFile::CANCELED:
357  case ToxFile::BROKEN:
358  case ToxFile::FINISHED:
359  active = false;
360  disconnect(&coreFile, nullptr, this, nullptr);
361  break;
363  case ToxFile::PAUSED:
365  break;
366  default:
367  qCritical() << "Invalid file status";
368  assert(false);
369  }
370 }
371 
373 {
374  if (lastStatus == file.status && file.status != ToxFile::PAUSED) {
375  return;
376  }
377 
378  switch (file.status) {
380  ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/pause.svg")));
381  ui->leftButton->setObjectName("pause");
382  ui->leftButton->setToolTip(tr("Pause transfer"));
383 
384  ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/no.svg")));
385  ui->rightButton->setObjectName("cancel");
386  ui->rightButton->setToolTip(tr("Cancel transfer"));
387 
389  break;
390 
391  case ToxFile::PAUSED:
392  if (file.pauseStatus.localPaused()) {
393  ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/arrow_white.svg")));
394  ui->leftButton->setObjectName("resume");
395  ui->leftButton->setToolTip(tr("Resume transfer"));
396  } else {
397  ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/pause.svg")));
398  ui->leftButton->setObjectName("pause");
399  ui->leftButton->setToolTip(tr("Pause transfer"));
400  }
401 
402  ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/no.svg")));
403  ui->rightButton->setObjectName("cancel");
404  ui->rightButton->setToolTip(tr("Cancel transfer"));
405 
407  break;
408 
410  ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/no.svg")));
411  ui->rightButton->setObjectName("cancel");
412  ui->rightButton->setToolTip(tr("Cancel transfer"));
413 
414  if (file.direction == ToxFile::SENDING) {
415  ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/pause.svg")));
416  ui->leftButton->setObjectName("pause");
417  ui->leftButton->setToolTip(tr("Pause transfer"));
418  } else {
419  ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/yes.svg")));
420  ui->leftButton->setObjectName("accept");
421  ui->leftButton->setToolTip(tr("Accept transfer"));
422  }
423  break;
424  case ToxFile::CANCELED:
425  case ToxFile::BROKEN:
426  ui->leftButton->hide();
427  ui->rightButton->hide();
428  break;
429  case ToxFile::FINISHED:
430  ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/yes.svg")));
431  ui->leftButton->setObjectName("ok");
432  ui->leftButton->setToolTip(tr("Open file"));
433  ui->leftButton->show();
434 
435  ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/dir.svg")));
436  ui->rightButton->setObjectName("dir");
437  ui->rightButton->setToolTip(tr("Open file directory"));
438  ui->rightButton->show();
439 
440  break;
441  default:
442  qCritical() << "Invalid file status";
443  assert(false);
444  }
445 }
446 
447 void FileTransferWidget::handleButton(QPushButton* btn)
448 {
450  if (btn->objectName() == "cancel") {
452  } else if (btn->objectName() == "pause") {
454  } else if (btn->objectName() == "resume") {
456  }
457  } else // receiving or paused
458  {
459  if (btn->objectName() == "cancel") {
461  } else if (btn->objectName() == "pause") {
463  } else if (btn->objectName() == "resume") {
465  } else if (btn->objectName() == "accept") {
466  QString path =
467  QFileDialog::getSaveFileName(Q_NULLPTR,
468  tr("Save a file", "Title of the file saving dialog"),
469  Settings::getInstance().getGlobalAutoAcceptDir() + "/"
470  + fileInfo.fileName);
471  acceptTransfer(path);
472  }
473  }
474 
475  if (btn->objectName() == "ok" || btn->objectName() == "previewButton") {
477  } else if (btn->objectName() == "dir") {
478  QString dirPath = QFileInfo(fileInfo.filePath).dir().path();
479  QDesktopServices::openUrl(QUrl::fromLocalFile(dirPath));
480  }
481 }
482 
483 void FileTransferWidget::showPreview(const QString& filename)
484 {
485  ui->previewButton->setIconFromFile(filename);
486  ui->previewButton->show();
487 }
488 
490 {
491  handleButton(ui->leftButton);
492 }
493 
495 {
496  handleButton(ui->rightButton);
497 }
498 
500 {
501  handleButton(ui->previewButton);
502 }
503 
505 {
506  assert(file == fileInfo);
507 
508  fileInfo = file;
509 
510  bool shouldUpdateFileProgress = file.status != ToxFile::TRANSMITTING || lastTransmissionUpdate ==
511  QTime() || lastTransmissionUpdate.msecsTo(file.progress.lastSampleTime()) > 1000;
512 
514  if (shouldUpdateFileProgress)
520 
521  lastStatus = file.status;
522 
523  if (shouldUpdateFileProgress) {
524  lastTransmissionUpdate = QTime::currentTime();
525  update();
526  }
527 }
528 
530 {
531  switch (status) {
533  case ToxFile::PAUSED:
536  break;
537  case ToxFile::BROKEN:
538  case ToxFile::CANCELED:
540  break;
541  case ToxFile::FINISHED:
543  break;
544  default:
545  qCritical() << "Invalid file status";
546  assert(false);
547  }
548 }
FileTransferWidget::~FileTransferWidget
virtual ~FileTransferWidget()
Definition: filetransferwidget.cpp:103
Style::TransferGood
@ TransferGood
Definition: style.h:33
style.h
FileTransferWidget::handleButton
void handleButton(QPushButton *btn)
Definition: filetransferwidget.cpp:447
ToxFile::BROKEN
@ BROKEN
Definition: toxfile.h:41
FileTransferWidget::updateFileProgress
void updateFileProgress(ToxFile const &file)
Definition: filetransferwidget.cpp:309
FileTransferWidget::setButtonColor
void setButtonColor(const QColor &c)
Definition: filetransferwidget.cpp:167
settings.h
FileTransferWidget::reloadTheme
void reloadTheme()
Definition: filetransferwidget.cpp:233
ToxFile::fileName
QString fileName
Definition: toxfile.h:67
ToxFile::fileNum
uint32_t fileNum
Definition: toxfile.h:65
FileTransferWidget::buttonColorAnimation
QVariantAnimation * buttonColorAnimation
Definition: filetransferwidget.h:83
ToxFile::TRANSMITTING
@ TRANSMITTING
Definition: toxfile.h:40
HistMessageContentType::file
@ file
ToxFile::FileStatus
FileStatus
Definition: toxfile.h:36
Widget::confirmExecutableOpen
static void confirmExecutableOpen(const QFileInfo &file)
Definition: widget.cpp:911
FileTransferWidget::setupButtons
void setupButtons(ToxFile const &file)
Definition: filetransferwidget.cpp:372
FileTransferWidget::onPreviewButtonClicked
void onPreviewButtonClicked()
Definition: filetransferwidget.cpp:499
FileTransferWidget::lastTransmissionUpdate
QTime lastTransmissionUpdate
Definition: filetransferwidget.h:89
CoreFile::cancelFileSend
void cancelFileSend(uint32_t friendId, uint32_t fileId)
Definition: corefile.cpp:208
Style::TransferMiddle
@ TransferMiddle
Definition: style.h:36
Style::getImagePath
static const QString getImagePath(const QString &filename)
Definition: style.cpp:182
FileTransferWidget::lastStatus
ToxFile::FileStatus lastStatus
Definition: filetransferwidget.h:90
FileTransferWidget::updateWidgetColor
void updateWidgetColor(ToxFile const &file)
Definition: filetransferwidget.cpp:238
FileTransferWidget::buttonBackgroundColor
QColor buttonBackgroundColor
Definition: filetransferwidget.h:86
ToxFile::direction
FileDirection direction
Definition: toxfile.h:71
FileTransferWidget::buttonColor
QColor buttonColor
Definition: filetransferwidget.h:85
FileTransferWidget::FileTransferWidget
FileTransferWidget(QWidget *parent, CoreFile &_coreFile, ToxFile file)
Definition: filetransferwidget.cpp:51
FileTransferWidget::active
bool active
Definition: filetransferwidget.h:88
FileTransferWidget::updateBackgroundColor
void updateBackgroundColor(const ToxFile::FileStatus status)
Definition: filetransferwidget.cpp:529
GUI::getInstance
static GUI & getInstance()
Returns the singleton instance.
Definition: gui.cpp:56
FileTransferWidget::updateWidget
void updateWidget(ToxFile const &file)
Definition: filetransferwidget.cpp:504
FileTransferWidget::coreFile
CoreFile & coreFile
Definition: filetransferwidget.h:79
FileTransferWidget::backgroundColor
QColor backgroundColor
Definition: filetransferwidget.h:84
FileTransferWidget::tryRemoveFile
static bool tryRemoveFile(const QString &filepath)
Dangerous way to find out if a path is writable.
Definition: filetransferwidget.cpp:114
Style::TransferWait
@ TransferWait
Definition: style.h:34
filetransferwidget.h
widget.h
ToxFile::INITIALIZING
@ INITIALIZING
Definition: toxfile.h:38
ToxFile::PAUSED
@ PAUSED
Definition: toxfile.h:39
ToxFile::CANCELED
@ CANCELED
Definition: toxfile.h:42
CoreFile::pauseResumeFile
void pauseResumeFile(uint32_t friendId, uint32_t fileId)
Definition: corefile.cpp:173
FileTransferWidget::acceptTransfer
void acceptTransfer(const QString &filepath)
Definition: filetransferwidget.cpp:132
ToxFile::FINISHED
@ FINISHED
Definition: toxfile.h:43
FileTransferWidget::ui
Ui::FileTransferWidget * ui
Definition: filetransferwidget.h:80
GUI::themeReload
void themeReload()
ToxFile::SENDING
@ SENDING
Definition: toxfile.h:50
FileTransferWidget::updateSignals
void updateSignals(ToxFile const &file)
Definition: filetransferwidget.cpp:349
FileTransferWidget::fileInfo
ToxFile fileInfo
Definition: filetransferwidget.h:81
FileTransferWidget::updatePreview
void updatePreview(ToxFile const &file)
Definition: filetransferwidget.cpp:284
CoreFile::cancelFileRecv
void cancelFileRecv(uint32_t friendId, uint32_t fileId)
Definition: corefile.cpp:224
Style::repolish
static void repolish(QWidget *w)
Definition: style.cpp:319
FileTransferWidget::onLeftButtonClicked
void onLeftButtonClicked()
Definition: filetransferwidget.cpp:489
Style::getColor
static QColor getColor(ColorPalette entry)
Definition: style.cpp:209
Settings::getInstance
static Settings & getInstance()
Returns the singleton instance.
Definition: settings.cpp:88
Style::GroundBase
@ GroundBase
Definition: style.h:41
exiftransform.h
FileTransferWidget::drawButtonAreaNeeded
bool drawButtonAreaNeeded() const
Definition: filetransferwidget.cpp:176
corefile.h
FileTransferWidget::showPreview
void showPreview(const QString &filename)
Definition: filetransferwidget.cpp:483
GUI::showWarning
static void showWarning(const QString &title, const QString &msg)
Show a warning to the user.
Definition: gui.cpp:130
Style::getStylesheet
static const QString getStylesheet(const QString &filename, const QFont &baseFont=QFont())
Definition: style.cpp:165
FileTransferWidget::setBackgroundColor
void setBackgroundColor(const QColor &c, bool whiteFont)
Definition: filetransferwidget.cpp:151
CoreFile::acceptFileRecvRequest
void acceptFileRecvRequest(uint32_t friendId, uint32_t fileId, QString path)
Definition: corefile.cpp:254
FileTransferWidget::onFileTransferUpdate
void onFileTransferUpdate(ToxFile file)
Definition: filetransferwidget.cpp:122
FileTransferWidget::isActive
bool isActive() const
Definition: filetransferwidget.cpp:127
ToxFile::friendId
uint32_t friendId
Definition: toxfile.h:66
FileTransferWidget::backgroundColorAnimation
QVariantAnimation * backgroundColorAnimation
Definition: filetransferwidget.h:82
gui.h
FileTransferWidget::paintEvent
void paintEvent(QPaintEvent *) final
Definition: filetransferwidget.cpp:182
ToxFile
Definition: toxfile.h:32
ToxFile::filePath
QString filePath
Definition: toxfile.h:68
Style::TransferBad
@ TransferBad
Definition: style.h:35
CoreFile
Manages the file transfer service of toxcore.
Definition: corefile.h:46
FileTransferWidget::onRightButtonClicked
void onRightButtonClicked()
Definition: filetransferwidget.cpp:494
FileTransferWidget::updateWidgetText
void updateWidgetText(ToxFile const &file)
Definition: filetransferwidget.cpp:247