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/core.h"
24 #include "src/core/corefile.h"
25 #include "src/persistence/settings.h"
26 #include "src/widget/gui.h"
27 #include "src/widget/style.h"
28 #include "src/widget/widget.h"
29
30 #include <libexif/exif-loader.h>
31
32 #include <QBuffer>
33 #include <QDebug>
34 #include <QDesktopServices>
35 #include <QDesktopWidget>
36 #include <QFile>
37 #include <QFileDialog>
38 #include <QMessageBox>
39 #include <QMouseEvent>
40 #include <QPainter>
41 #include <QVariantAnimation>
42
43 #include <cassert>
44 #include <math.h>
45
46
47 // The leftButton is used to accept, pause, or resume a file transfer, as well as to open a
48 // received file.
49 // The rightButton is used to cancel a file transfer, or to open the directory a file was
50 // downloaded to.
51
FileTransferWidget(QWidget * parent,ToxFile file)52 FileTransferWidget::FileTransferWidget(QWidget* parent, ToxFile file)
53 : QWidget(parent)
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.filesize));
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 CoreFile* coreFile = Core::getInstance()->getCoreFile();
90
91 connect(ui->leftButton, &QPushButton::clicked, this, &FileTransferWidget::onLeftButtonClicked);
92 connect(ui->rightButton, &QPushButton::clicked, this, &FileTransferWidget::onRightButtonClicked);
93 connect(ui->previewButton, &QPushButton::clicked, this,
94 &FileTransferWidget::onPreviewButtonClicked);
95
96 // Set lastStatus to anything but the file's current value, this forces an update
97 lastStatus = file.status == ToxFile::FINISHED ? ToxFile::INITIALIZING : ToxFile::FINISHED;
98 updateWidget(file);
99
100 setFixedHeight(64);
101 }
102
~FileTransferWidget()103 FileTransferWidget::~FileTransferWidget()
104 {
105 delete ui;
106 }
107
108 // TODO(sudden6): remove file IO from the UI
109 /**
110 * @brief Dangerous way to find out if a path is writable.
111 * @param filepath Path to file which should be deleted.
112 * @return True, if file writeable, false otherwise.
113 */
tryRemoveFile(const QString & filepath)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
onFileTransferUpdate(ToxFile file)122 void FileTransferWidget::onFileTransferUpdate(ToxFile file)
123 {
124 updateWidget(file);
125 }
126
isActive() const127 bool FileTransferWidget::isActive() const
128 {
129 return active;
130 }
131
acceptTransfer(const QString & filepath)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!
148 CoreFile* coreFile = Core::getInstance()->getCoreFile();
149 coreFile->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath);
150 }
151
setBackgroundColor(const QColor & c,bool whiteFont)152 void FileTransferWidget::setBackgroundColor(const QColor& c, bool whiteFont)
153 {
154 if (c != backgroundColor) {
155 backgroundColorAnimation->setStartValue(backgroundColor);
156 backgroundColorAnimation->setEndValue(c);
157 backgroundColorAnimation->start();
158 }
159
160 setProperty("fontColor", whiteFont ? "white" : "black");
161
162 setStyleSheet(Style::getStylesheet("fileTransferInstance/filetransferWidget.css"));
163 Style::repolish(this);
164
165 update();
166 }
167
setButtonColor(const QColor & c)168 void FileTransferWidget::setButtonColor(const QColor& c)
169 {
170 if (c != buttonColor) {
171 buttonColorAnimation->setStartValue(buttonColor);
172 buttonColorAnimation->setEndValue(c);
173 buttonColorAnimation->start();
174 }
175 }
176
drawButtonAreaNeeded() const177 bool FileTransferWidget::drawButtonAreaNeeded() const
178 {
179 return (ui->rightButton->isVisible() || ui->leftButton->isVisible())
180 && !(ui->leftButton->isVisible() && ui->leftButton->objectName() == "ok");
181 }
182
paintEvent(QPaintEvent *)183 void FileTransferWidget::paintEvent(QPaintEvent*)
184 {
185 // required by Hi-DPI support as border-image doesn't work.
186 QPainter painter(this);
187 painter.setRenderHint(QPainter::Antialiasing);
188 painter.setPen(Qt::NoPen);
189
190 qreal ratio = static_cast<qreal>(geometry().height()) / static_cast<qreal>(geometry().width());
191 const int r = 24;
192 const int buttonFieldWidth = 32;
193 const int lineWidth = 1;
194
195 // Draw the widget background:
196 painter.setClipRect(QRect(0, 0, width(), height()));
197 painter.setBrush(QBrush(backgroundColor));
198 painter.drawRoundedRect(geometry(), r * ratio, r, Qt::RelativeSize);
199
200 if (drawButtonAreaNeeded()) {
201 // Draw the button background:
202 QPainterPath buttonBackground;
203 buttonBackground.addRoundedRect(width() - 2 * buttonFieldWidth - lineWidth * 2, 0,
204 buttonFieldWidth, buttonFieldWidth + lineWidth, 50, 50,
205 Qt::RelativeSize);
206 buttonBackground.addRect(width() - 2 * buttonFieldWidth - lineWidth * 2, 0,
207 buttonFieldWidth * 2, buttonFieldWidth / 2);
208 buttonBackground.addRect(width() - 1.5 * buttonFieldWidth - lineWidth * 2, 0,
209 buttonFieldWidth * 2, buttonFieldWidth + 1);
210 buttonBackground.setFillRule(Qt::WindingFill);
211 painter.setBrush(QBrush(buttonBackgroundColor));
212 painter.drawPath(buttonBackground);
213
214 // Draw the left button:
215 QPainterPath leftButton;
216 leftButton.addRoundedRect(QRect(width() - 2 * buttonFieldWidth - lineWidth, 0,
217 buttonFieldWidth, buttonFieldWidth),
218 50, 50, Qt::RelativeSize);
219 leftButton.addRect(QRect(width() - 2 * buttonFieldWidth - lineWidth, 0,
220 buttonFieldWidth / 2, buttonFieldWidth / 2));
221 leftButton.addRect(QRect(width() - 1.5 * buttonFieldWidth - lineWidth, 0,
222 buttonFieldWidth / 2, buttonFieldWidth));
223 leftButton.setFillRule(Qt::WindingFill);
224 painter.setBrush(QBrush(buttonColor));
225 painter.drawPath(leftButton);
226
227 // Draw the right button:
228 painter.setBrush(QBrush(buttonColor));
229 painter.setClipRect(QRect(width() - buttonFieldWidth, 0, buttonFieldWidth, buttonFieldWidth));
230 painter.drawRoundedRect(geometry(), r * ratio, r, Qt::RelativeSize);
231 }
232 }
233
getHumanReadableSize(qint64 size)234 QString FileTransferWidget::getHumanReadableSize(qint64 size)
235 {
236 static const char* suffix[] = {"B", "kiB", "MiB", "GiB", "TiB"};
237 int exp = 0;
238
239 if (size > 0) {
240 exp = std::min((int)(log(size) / log(1024)), (int)(sizeof(suffix) / sizeof(suffix[0]) - 1));
241 }
242
243 return QString().setNum(size / pow(1024, exp), 'f', exp > 1 ? 2 : 0).append(suffix[exp]);
244 }
245
updateWidgetColor(ToxFile const & file)246 void FileTransferWidget::updateWidgetColor(ToxFile const& file)
247 {
248 if (lastStatus == file.status) {
249 return;
250 }
251
252 switch (file.status) {
253 case ToxFile::INITIALIZING:
254 case ToxFile::PAUSED:
255 case ToxFile::TRANSMITTING:
256 setBackgroundColor(Style::getColor(Style::TransferMiddle), false);
257 break;
258 case ToxFile::BROKEN:
259 case ToxFile::CANCELED:
260 setBackgroundColor(Style::getColor(Style::TransferBad), true);
261 break;
262 case ToxFile::FINISHED:
263 setBackgroundColor(Style::getColor(Style::TransferGood), true);
264 break;
265 default:
266 qCritical() << "Invalid file status";
267 assert(false);
268 }
269 }
270
updateWidgetText(ToxFile const & file)271 void FileTransferWidget::updateWidgetText(ToxFile const& file)
272 {
273 if (lastStatus == file.status && file.status != ToxFile::PAUSED) {
274 return;
275 }
276
277 switch (file.status) {
278 case ToxFile::INITIALIZING:
279 if (file.direction == ToxFile::SENDING) {
280 ui->progressLabel->setText(tr("Waiting to send...", "file transfer widget"));
281 } else {
282 ui->progressLabel->setText(tr("Accept to receive this file", "file transfer widget"));
283 }
284 break;
285 case ToxFile::PAUSED:
286 ui->etaLabel->setText("");
287 if (file.pauseStatus.localPaused()) {
288 ui->progressLabel->setText(tr("Paused", "file transfer widget"));
289 } else {
290 ui->progressLabel->setText(tr("Remote Paused", "file transfer widget"));
291 }
292 break;
293 case ToxFile::TRANSMITTING:
294 ui->etaLabel->setText("");
295 ui->progressLabel->setText(tr("Resuming...", "file transfer widget"));
296 break;
297 case ToxFile::BROKEN:
298 case ToxFile::CANCELED:
299 break;
300 case ToxFile::FINISHED:
301 break;
302 default:
303 qCritical() << "Invalid file status";
304 assert(false);
305 }
306 }
307
updatePreview(ToxFile const & file)308 void FileTransferWidget::updatePreview(ToxFile const& file)
309 {
310 if (lastStatus == file.status) {
311 return;
312 }
313
314 switch (file.status) {
315 case ToxFile::INITIALIZING:
316 case ToxFile::PAUSED:
317 case ToxFile::TRANSMITTING:
318 case ToxFile::BROKEN:
319 case ToxFile::CANCELED:
320 if (file.direction == ToxFile::SENDING) {
321 showPreview(file.filePath);
322 }
323 break;
324 case ToxFile::FINISHED:
325 showPreview(file.filePath);
326 break;
327 default:
328 qCritical() << "Invalid file status";
329 assert(false);
330 }
331 }
332
updateFileProgress(ToxFile const & file)333 void FileTransferWidget::updateFileProgress(ToxFile const& file)
334 {
335 switch (file.status) {
336 case ToxFile::INITIALIZING:
337 break;
338 case ToxFile::PAUSED:
339 fileProgress.resetSpeed();
340 break;
341 case ToxFile::TRANSMITTING: {
342 if (!fileProgress.needsUpdate()) {
343 break;
344 }
345
346 fileProgress.addSample(file);
347 auto speed = fileProgress.getSpeed();
348 auto progress = fileProgress.getProgress();
349 auto remainingTime = fileProgress.getTimeLeftSeconds();
350
351 ui->progressBar->setValue(static_cast<int>(progress * 100.0));
352
353 // update UI
354 if (speed > 0) {
355 // ETA
356 QTime toGo = QTime(0, 0).addSecs(remainingTime);
357 QString format = toGo.hour() > 0 ? "hh:mm:ss" : "mm:ss";
358 ui->etaLabel->setText(toGo.toString(format));
359 } else {
360 ui->etaLabel->setText("");
361 }
362
363 ui->progressLabel->setText(getHumanReadableSize(speed) + "/s");
364 break;
365 }
366 case ToxFile::BROKEN:
367 case ToxFile::CANCELED:
368 case ToxFile::FINISHED: {
369 ui->progressBar->hide();
370 ui->progressLabel->hide();
371 ui->etaLabel->hide();
372 break;
373 }
374 default:
375 qCritical() << "Invalid file status";
376 assert(false);
377 }
378 }
379
updateSignals(ToxFile const & file)380 void FileTransferWidget::updateSignals(ToxFile const& file)
381 {
382 if (lastStatus == file.status) {
383 return;
384 }
385
386 switch (file.status) {
387 case ToxFile::CANCELED:
388 case ToxFile::BROKEN:
389 case ToxFile::FINISHED:
390 active = false;
391 disconnect(Core::getInstance()->getCoreFile(), nullptr, this, nullptr);
392 break;
393 case ToxFile::INITIALIZING:
394 case ToxFile::PAUSED:
395 case ToxFile::TRANSMITTING:
396 break;
397 default:
398 qCritical() << "Invalid file status";
399 assert(false);
400 }
401 }
402
setupButtons(ToxFile const & file)403 void FileTransferWidget::setupButtons(ToxFile const& file)
404 {
405 if (lastStatus == file.status && file.status != ToxFile::PAUSED) {
406 return;
407 }
408
409 switch (file.status) {
410 case ToxFile::TRANSMITTING:
411 ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/pause.svg")));
412 ui->leftButton->setObjectName("pause");
413 ui->leftButton->setToolTip(tr("Pause transfer"));
414
415 ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/no.svg")));
416 ui->rightButton->setObjectName("cancel");
417 ui->rightButton->setToolTip(tr("Cancel transfer"));
418
419 setButtonColor(Style::getColor(Style::TransferGood));
420 break;
421
422 case ToxFile::PAUSED:
423 if (file.pauseStatus.localPaused()) {
424 ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/arrow_white.svg")));
425 ui->leftButton->setObjectName("resume");
426 ui->leftButton->setToolTip(tr("Resume transfer"));
427 } else {
428 ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/pause.svg")));
429 ui->leftButton->setObjectName("pause");
430 ui->leftButton->setToolTip(tr("Pause transfer"));
431 }
432
433 ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/no.svg")));
434 ui->rightButton->setObjectName("cancel");
435 ui->rightButton->setToolTip(tr("Cancel transfer"));
436
437 setButtonColor(Style::getColor(Style::TransferMiddle));
438 break;
439
440 case ToxFile::INITIALIZING:
441 ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/no.svg")));
442 ui->rightButton->setObjectName("cancel");
443 ui->rightButton->setToolTip(tr("Cancel transfer"));
444
445 if (file.direction == ToxFile::SENDING) {
446 ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/pause.svg")));
447 ui->leftButton->setObjectName("pause");
448 ui->leftButton->setToolTip(tr("Pause transfer"));
449 } else {
450 ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/yes.svg")));
451 ui->leftButton->setObjectName("accept");
452 ui->leftButton->setToolTip(tr("Accept transfer"));
453 }
454 break;
455 case ToxFile::CANCELED:
456 case ToxFile::BROKEN:
457 ui->leftButton->hide();
458 ui->rightButton->hide();
459 break;
460 case ToxFile::FINISHED:
461 ui->leftButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/yes.svg")));
462 ui->leftButton->setObjectName("ok");
463 ui->leftButton->setToolTip(tr("Open file"));
464 ui->leftButton->show();
465
466 ui->rightButton->setIcon(QIcon(Style::getImagePath("fileTransferInstance/dir.svg")));
467 ui->rightButton->setObjectName("dir");
468 ui->rightButton->setToolTip(tr("Open file directory"));
469 ui->rightButton->show();
470
471 break;
472 default:
473 qCritical() << "Invalid file status";
474 assert(false);
475 }
476 }
477
handleButton(QPushButton * btn)478 void FileTransferWidget::handleButton(QPushButton* btn)
479 {
480 CoreFile* coreFile = Core::getInstance()->getCoreFile();
481 if (fileInfo.direction == ToxFile::SENDING) {
482 if (btn->objectName() == "cancel") {
483 coreFile->cancelFileSend(fileInfo.friendId, fileInfo.fileNum);
484 } else if (btn->objectName() == "pause") {
485 coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum);
486 } else if (btn->objectName() == "resume") {
487 coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum);
488 }
489 } else // receiving or paused
490 {
491 if (btn->objectName() == "cancel") {
492 coreFile->cancelFileRecv(fileInfo.friendId, fileInfo.fileNum);
493 } else if (btn->objectName() == "pause") {
494 coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum);
495 } else if (btn->objectName() == "resume") {
496 coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum);
497 } else if (btn->objectName() == "accept") {
498 QString path =
499 QFileDialog::getSaveFileName(Q_NULLPTR,
500 tr("Save a file", "Title of the file saving dialog"),
501 Settings::getInstance().getGlobalAutoAcceptDir() + "/"
502 + fileInfo.fileName);
503 acceptTransfer(path);
504 }
505 }
506
507 if (btn->objectName() == "ok" || btn->objectName() == "previewButton") {
508 Widget::confirmExecutableOpen(QFileInfo(fileInfo.filePath));
509 } else if (btn->objectName() == "dir") {
510 QString dirPath = QFileInfo(fileInfo.filePath).dir().path();
511 QDesktopServices::openUrl(QUrl::fromLocalFile(dirPath));
512 }
513 }
514
showPreview(const QString & filename)515 void FileTransferWidget::showPreview(const QString& filename)
516 {
517 static const QStringList previewExtensions = {"png", "jpeg", "jpg", "gif", "svg",
518 "PNG", "JPEG", "JPG", "GIF", "SVG"};
519
520 if (previewExtensions.contains(QFileInfo(filename).suffix())) {
521 // Subtract to make border visible
522 const int size = qMax(ui->previewButton->width(), ui->previewButton->height()) - 4;
523
524 QFile imageFile(filename);
525 if (!imageFile.open(QIODevice::ReadOnly)) {
526 return;
527 }
528 const QByteArray imageFileData = imageFile.readAll();
529 QImage image = QImage::fromData(imageFileData);
530 const int exifOrientation =
531 getExifOrientation(imageFileData.constData(), imageFileData.size());
532 if (exifOrientation) {
533 applyTransformation(exifOrientation, image);
534 }
535
536 const QPixmap iconPixmap = scaleCropIntoSquare(QPixmap::fromImage(image), size);
537
538 ui->previewButton->setIcon(QIcon(iconPixmap));
539 ui->previewButton->setIconSize(iconPixmap.size());
540 ui->previewButton->show();
541 // Show mouseover preview, but make sure it's not larger than 50% of the screen
542 // width/height
543 const QRect desktopSize = QApplication::desktop()->geometry();
544 const int maxPreviewWidth{desktopSize.width() / 2};
545 const int maxPreviewHeight{desktopSize.height() / 2};
546 const QImage previewImage = [&image, maxPreviewWidth, maxPreviewHeight]() {
547 if (image.width() > maxPreviewWidth || image.height() > maxPreviewHeight) {
548 return image.scaled(maxPreviewWidth, maxPreviewHeight, Qt::KeepAspectRatio,
549 Qt::SmoothTransformation);
550 } else {
551 return image;
552 }
553 }();
554
555 QByteArray imageData;
556 QBuffer buffer(&imageData);
557 buffer.open(QIODevice::WriteOnly);
558 previewImage.save(&buffer, "PNG");
559 buffer.close();
560 ui->previewButton->setToolTip("<img src=data:image/png;base64," + imageData.toBase64() + "/>");
561 }
562 }
563
onLeftButtonClicked()564 void FileTransferWidget::onLeftButtonClicked()
565 {
566 handleButton(ui->leftButton);
567 }
568
onRightButtonClicked()569 void FileTransferWidget::onRightButtonClicked()
570 {
571 handleButton(ui->rightButton);
572 }
573
onPreviewButtonClicked()574 void FileTransferWidget::onPreviewButtonClicked()
575 {
576 handleButton(ui->previewButton);
577 }
578
scaleCropIntoSquare(const QPixmap & source,const int targetSize)579 QPixmap FileTransferWidget::scaleCropIntoSquare(const QPixmap& source, const int targetSize)
580 {
581 QPixmap result;
582
583 // Make sure smaller-than-icon images (at least one dimension is smaller) will not be
584 // upscaled
585 if (source.width() < targetSize || source.height() < targetSize) {
586 result = source;
587 } else {
588 result = source.scaled(targetSize, targetSize, Qt::KeepAspectRatioByExpanding,
589 Qt::SmoothTransformation);
590 }
591
592 // Then, image has to be cropped (if needed) so it will not overflow rectangle
593 // Only one dimension will be bigger after Qt::KeepAspectRatioByExpanding
594 if (result.width() > targetSize) {
595 return result.copy((result.width() - targetSize) / 2, 0, targetSize, targetSize);
596 } else if (result.height() > targetSize) {
597 return result.copy(0, (result.height() - targetSize) / 2, targetSize, targetSize);
598 }
599
600 // Picture was rectangle in the first place, no cropping
601 return result;
602 }
603
getExifOrientation(const char * data,const int size)604 int FileTransferWidget::getExifOrientation(const char* data, const int size)
605 {
606 ExifData* exifData = exif_data_new_from_data(reinterpret_cast<const unsigned char*>(data), size);
607
608 if (!exifData) {
609 return 0;
610 }
611
612 int orientation = 0;
613 const ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
614 const ExifEntry* const exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
615 if (exifEntry) {
616 orientation = exif_get_short(exifEntry->data, byteOrder);
617 }
618 exif_data_free(exifData);
619 return orientation;
620 }
621
applyTransformation(const int orientation,QImage & image)622 void FileTransferWidget::applyTransformation(const int orientation, QImage& image)
623 {
624 QTransform exifTransform;
625 switch (static_cast<ExifOrientation>(orientation)) {
626 case ExifOrientation::TopLeft:
627 break;
628 case ExifOrientation::TopRight:
629 image = image.mirrored(1, 0);
630 break;
631 case ExifOrientation::BottomRight:
632 exifTransform.rotate(180);
633 break;
634 case ExifOrientation::BottomLeft:
635 image = image.mirrored(0, 1);
636 break;
637 case ExifOrientation::LeftTop:
638 exifTransform.rotate(90);
639 image = image.mirrored(0, 1);
640 break;
641 case ExifOrientation::RightTop:
642 exifTransform.rotate(-90);
643 break;
644 case ExifOrientation::RightBottom:
645 exifTransform.rotate(-90);
646 image = image.mirrored(0, 1);
647 break;
648 case ExifOrientation::LeftBottom:
649 exifTransform.rotate(90);
650 break;
651 default:
652 qWarning() << "Invalid exif orientation passed to applyTransformation!";
653 }
654 image = image.transformed(exifTransform);
655 }
656
updateWidget(ToxFile const & file)657 void FileTransferWidget::updateWidget(ToxFile const& file)
658 {
659 assert(file == fileInfo);
660
661 fileInfo = file;
662
663 // If we repainted on every packet our gui would be *very* slow
664 bool bTransmitNeedsUpdate = fileProgress.needsUpdate();
665
666 updatePreview(file);
667 updateFileProgress(file);
668 updateWidgetText(file);
669 updateWidgetColor(file);
670 setupButtons(file);
671 updateSignals(file);
672
673 lastStatus = file.status;
674
675 // trigger repaint
676 switch (file.status) {
677 case ToxFile::TRANSMITTING:
678 if (!bTransmitNeedsUpdate) {
679 break;
680 }
681 // fallthrough
682 default:
683 update();
684 }
685 }
686