1 // SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
2 // SPDX-FileCopyrightText: 2021 Nheko Contributors
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 
6 #include <QBuffer>
7 #include <QFile>
8 #include <QFileInfo>
9 #include <QHBoxLayout>
10 #include <QMimeDatabase>
11 #include <QVBoxLayout>
12 
13 #include "dialogs/PreviewUploadOverlay.h"
14 
15 #include "Config.h"
16 #include "Logging.h"
17 #include "MainWindow.h"
18 #include "Utils.h"
19 
20 using namespace dialogs;
21 
22 constexpr const char *DEFAULT = "Upload %1?";
23 constexpr const char *ERR_MSG = "Failed to load image type '%1'. Continue upload?";
24 
PreviewUploadOverlay(QWidget * parent)25 PreviewUploadOverlay::PreviewUploadOverlay(QWidget *parent)
26   : QWidget{parent}
27   , titleLabel_{this}
28   , fileName_{this}
29   , upload_{tr("Upload"), this}
30   , cancel_{tr("Cancel"), this}
31 {
32     auto hlayout = new QHBoxLayout;
33     hlayout->addStretch(1);
34     hlayout->addWidget(&cancel_);
35     hlayout->addWidget(&upload_);
36     hlayout->setMargin(0);
37 
38     auto vlayout = new QVBoxLayout{this};
39     vlayout->addWidget(&titleLabel_);
40     vlayout->addWidget(&infoLabel_);
41     vlayout->addWidget(&fileName_);
42     vlayout->addLayout(hlayout);
43     vlayout->setSpacing(conf::modals::WIDGET_SPACING);
44     vlayout->setMargin(conf::modals::WIDGET_MARGIN);
45 
46     upload_.setDefault(true);
__anon9b508dd00102() 47     connect(&upload_, &QPushButton::clicked, [this]() {
48         emit confirmUpload(data_, mediaType_, fileName_.text());
49         close();
50     });
51 
__anon9b508dd00202() 52     connect(&fileName_, &QLineEdit::returnPressed, this, [this]() {
53         emit confirmUpload(data_, mediaType_, fileName_.text());
54         close();
55     });
56 
__anon9b508dd00302() 57     connect(&cancel_, &QPushButton::clicked, this, [this]() {
58         emit aborted();
59         close();
60     });
61 }
62 
63 void
init()64 PreviewUploadOverlay::init()
65 {
66     QSize winsize;
67     QPoint center;
68 
69     auto window = MainWindow::instance();
70     if (window) {
71         winsize = window->frameGeometry().size();
72         center  = window->frameGeometry().center();
73     } else {
74         nhlog::ui()->warn("unable to retrieve MainWindow's size");
75     }
76 
77     fileName_.setText(QFileInfo{filePath_}.fileName());
78 
79     setAutoFillBackground(true);
80     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
81     setWindowModality(Qt::WindowModal);
82 
83     QFont font;
84     font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO);
85 
86     titleLabel_.setFont(font);
87     titleLabel_.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
88     titleLabel_.setAlignment(Qt::AlignCenter);
89     infoLabel_.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
90     fileName_.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
91     fileName_.setAlignment(Qt::AlignCenter);
92     upload_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
93     cancel_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
94 
95     if (isImage_) {
96         infoLabel_.setAlignment(Qt::AlignCenter);
97 
98         const auto maxWidth  = winsize.width() * 0.8;
99         const auto maxHeight = winsize.height() * 0.8;
100 
101         // Scale image preview to fit into the application window.
102         infoLabel_.setPixmap(utils::scaleDown(maxWidth, maxHeight, image_));
103         move(center.x() - (width() * 0.5), center.y() - (height() * 0.5));
104     } else {
105         infoLabel_.setAlignment(Qt::AlignLeft);
106     }
107     infoLabel_.setScaledContents(false);
108 
109     show();
110 }
111 
112 void
setLabels(const QString & type,const QString & mime,uint64_t upload_size)113 PreviewUploadOverlay::setLabels(const QString &type, const QString &mime, uint64_t upload_size)
114 {
115     if (mediaType_.split('/')[0] == "image") {
116         if (!image_.loadFromData(data_)) {
117             titleLabel_.setText(QString{tr(ERR_MSG)}.arg(type));
118         } else {
119             titleLabel_.setText(QString{tr(DEFAULT)}.arg(mediaType_));
120         }
121         isImage_ = true;
122     } else {
123         auto const info = QString{tr("Media type: %1\n"
124                                      "Media size: %2\n")}
125                             .arg(mime)
126                             .arg(utils::humanReadableFileSize(upload_size));
127 
128         titleLabel_.setText(QString{tr(DEFAULT)}.arg("file"));
129         infoLabel_.setText(info);
130     }
131 }
132 
133 void
setPreview(const QImage & src,const QString & mime)134 PreviewUploadOverlay::setPreview(const QImage &src, const QString &mime)
135 {
136     nhlog::ui()->info(
137       "Pasting image with size: {}x{}, format: {}", src.height(), src.width(), mime.toStdString());
138 
139     auto const &split = mime.split('/');
140     auto const &type  = split[1];
141 
142     QBuffer buffer(&data_);
143     buffer.open(QIODevice::WriteOnly);
144     if (src.save(&buffer, type.toStdString().c_str()))
145         titleLabel_.setText(QString{tr(DEFAULT)}.arg("image"));
146     else
147         titleLabel_.setText(QString{tr(ERR_MSG)}.arg(type));
148 
149     mediaType_ = mime;
150     filePath_  = "clipboard." + type;
151     image_.convertFromImage(src);
152     isImage_ = true;
153 
154     titleLabel_.setText(QString{tr(DEFAULT)}.arg("image"));
155     init();
156 }
157 
158 void
setPreview(const QByteArray data,const QString & mime)159 PreviewUploadOverlay::setPreview(const QByteArray data, const QString &mime)
160 {
161     nhlog::ui()->info("Pasting {} bytes of data, mimetype {}", data.size(), mime.toStdString());
162 
163     auto const &split = mime.split('/');
164     auto const &type  = split[1];
165 
166     data_      = data;
167     mediaType_ = mime;
168     filePath_  = "clipboard." + type;
169     isImage_   = false;
170 
171     if (mime == "image/svg+xml") {
172         isImage_ = true;
173         image_.loadFromData(data_, mediaType_.toStdString().c_str());
174     }
175 
176     setLabels(type, mime, data_.size());
177     init();
178 }
179 
180 void
setPreview(const QString & path)181 PreviewUploadOverlay::setPreview(const QString &path)
182 {
183     QFile file{path};
184 
185     if (!file.open(QIODevice::ReadOnly)) {
186         nhlog::ui()->warn(
187           "Failed to open file ({}): {}", path.toStdString(), file.errorString().toStdString());
188         close();
189         return;
190     }
191 
192     QMimeDatabase db;
193     auto mime = db.mimeTypeForFileNameAndData(path, &file);
194 
195     if ((data_ = file.readAll()).isEmpty()) {
196         nhlog::ui()->warn("Failed to read media: {}", file.errorString().toStdString());
197         close();
198         return;
199     }
200 
201     auto const &split = mime.name().split('/');
202 
203     mediaType_ = mime.name();
204     filePath_  = file.fileName();
205     isImage_   = false;
206 
207     setLabels(split[1], mime.name(), data_.size());
208     init();
209 }
210 
211 void
keyPressEvent(QKeyEvent * event)212 PreviewUploadOverlay::keyPressEvent(QKeyEvent *event)
213 {
214     if (event->matches(QKeySequence::Cancel)) {
215         emit aborted();
216         close();
217     } else {
218         QWidget::keyPressEvent(event);
219     }
220 }