diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index ace201eb..9ec377e8 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -230,21 +230,27 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
&TextInputWidget::uploadImage,
this,
[=](QSharedPointer<QIODevice> data, const QString &fn) {
- client_->uploadImage(current_room_, data, fn);
+ client_->uploadImage(current_room_, fn, data);
});
connect(text_input_,
&TextInputWidget::uploadFile,
this,
[=](QSharedPointer<QIODevice> data, const QString &fn) {
- client_->uploadFile(current_room_, data, fn);
+ client_->uploadFile(current_room_, fn, data);
});
connect(text_input_,
&TextInputWidget::uploadAudio,
this,
[=](QSharedPointer<QIODevice> data, const QString &fn) {
- client_->uploadAudio(current_room_, data, fn);
+ client_->uploadAudio(current_room_, fn, data);
+ });
+ connect(text_input_,
+ &TextInputWidget::uploadVideo,
+ this,
+ [=](QSharedPointer<QIODevice> data, const QString &fn) {
+ client_->uploadVideo(current_room_, fn, data);
});
connect(
@@ -253,23 +259,30 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
connect(client_.data(),
&MatrixClient::imageUploaded,
this,
- [=](QString roomid, QSharedPointer<QIODevice> data, QString filename, QString url) {
+ [=](QString roomid, QString filename, QString url, QString mime, int64_t dsize) {
text_input_->hideUploadSpinner();
- view_manager_->queueImageMessage(roomid, data, filename, url);
+ view_manager_->queueImageMessage(roomid, filename, url, mime, dsize);
});
connect(client_.data(),
&MatrixClient::fileUploaded,
this,
- [=](QString roomid, QString filename, QString url) {
+ [=](QString roomid, QString filename, QString url, QString mime, int64_t dsize) {
text_input_->hideUploadSpinner();
- view_manager_->queueFileMessage(roomid, filename, url);
+ view_manager_->queueFileMessage(roomid, filename, url, mime, dsize);
});
connect(client_.data(),
&MatrixClient::audioUploaded,
this,
- [=](QString roomid, QString filename, QString url) {
+ [=](QString roomid, QString filename, QString url, QString mime, int64_t dsize) {
+ text_input_->hideUploadSpinner();
+ view_manager_->queueAudioMessage(roomid, filename, url, mime, dsize);
+ });
+ connect(client_.data(),
+ &MatrixClient::videoUploaded,
+ this,
+ [=](QString roomid, QString filename, QString url, QString mime, int64_t dsize) {
text_input_->hideUploadSpinner();
- view_manager_->queueAudioMessage(roomid, filename, url);
+ view_manager_->queueVideoMessage(roomid, filename, url, mime, dsize);
});
connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar);
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 1d42e36c..c915c74a 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -280,7 +280,8 @@ MatrixClient::sendRoomMessage(mtx::events::MessageType ty,
int txnId,
const QString &roomid,
const QString &msg,
- const QFileInfo &fileinfo,
+ const QString &mime,
+ const int64_t media_size,
const QString &url) noexcept
{
QUrlQuery query;
@@ -291,14 +292,8 @@ MatrixClient::sendRoomMessage(mtx::events::MessageType ty,
QString("/rooms/%1/send/m.room.message/%2").arg(roomid).arg(txnId));
endpoint.setQuery(query);
- QString msgType("");
-
- QMimeDatabase db;
- QMimeType mime =
- db.mimeTypeForFile(fileinfo.absoluteFilePath(), QMimeDatabase::MatchContent);
-
QJsonObject body;
- QJsonObject info = {{"size", fileinfo.size()}, {"mimetype", mime.name()}};
+ QJsonObject info = {{"size", static_cast<qint64>(media_size)}, {"mimetype", mime}};
switch (ty) {
case mtx::events::MessageType::Text:
@@ -316,6 +311,9 @@ MatrixClient::sendRoomMessage(mtx::events::MessageType ty,
case mtx::events::MessageType::Audio:
body = {{"msgtype", "m.audio"}, {"body", msg}, {"url", url}, {"info", info}};
break;
+ case mtx::events::MessageType::Video:
+ body = {{"msgtype", "m.video"}, {"body", msg}, {"url", url}, {"info", info}};
+ break;
default:
qDebug() << "SendRoomMessage: Unknown message type for" << msg;
return;
@@ -812,124 +810,97 @@ MatrixClient::messages(const QString &roomid, const QString &from_token, int lim
void
MatrixClient::uploadImage(const QString &roomid,
- const QSharedPointer<QIODevice> data,
- const QString &filename)
+ const QString &filename,
+ const QSharedPointer<QIODevice> data)
{
auto reply = makeUploadRequest(data);
if (reply == nullptr)
return;
- connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, data, filename]() {
- reply->deleteLater();
-
- int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- if (status == 0 || status >= 400) {
- emit syncFailed(reply->errorString());
- return;
- }
-
- auto res_data = reply->readAll();
-
- if (res_data.isEmpty())
+ connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename, data]() {
+ auto json = getUploadReply(reply);
+ if (json.isEmpty())
return;
- auto json = QJsonDocument::fromJson(res_data);
-
- if (!json.isObject()) {
- qDebug() << "Media upload: Response is not a json object.";
- return;
- }
+ auto mime = reply->request().header(QNetworkRequest::ContentTypeHeader).toString();
+ auto size =
+ reply->request().header(QNetworkRequest::ContentLengthHeader).toLongLong();
- QJsonObject object = json.object();
- if (!object.contains("content_uri")) {
- qDebug() << "Media upload: Missing content_uri key";
- qDebug() << object;
- return;
- }
-
- emit imageUploaded(roomid, data, filename, object.value("content_uri").toString());
+ emit imageUploaded(
+ roomid, filename, json.value("content_uri").toString(), mime, size);
});
}
void
MatrixClient::uploadFile(const QString &roomid,
- const QSharedPointer<QIODevice> data,
- const QString &filename)
+ const QString &filename,
+ const QSharedPointer<QIODevice> data)
{
auto reply = makeUploadRequest(data);
- connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, data, filename]() {
- reply->deleteLater();
-
- int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- if (status == 0 || status >= 400) {
- emit syncFailed(reply->errorString());
- return;
- }
-
- auto data = reply->readAll();
+ if (reply == nullptr)
+ return;
- if (data.isEmpty())
+ connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename, data]() {
+ auto json = getUploadReply(reply);
+ if (json.isEmpty())
return;
- auto json = QJsonDocument::fromJson(data);
+ auto mime = reply->request().header(QNetworkRequest::ContentTypeHeader).toString();
+ auto size =
+ reply->request().header(QNetworkRequest::ContentLengthHeader).toLongLong();
- if (!json.isObject()) {
- qDebug() << "Media upload: Response is not a json object.";
- return;
- }
-
- QJsonObject object = json.object();
- if (!object.contains("content_uri")) {
- qDebug() << "Media upload: Missing content_uri key";
- qDebug() << object;
- return;
- }
-
- emit fileUploaded(roomid, filename, object.value("content_uri").toString());
+ emit fileUploaded(
+ roomid, filename, json.value("content_uri").toString(), mime, size);
});
}
void
MatrixClient::uploadAudio(const QString &roomid,
- const QSharedPointer<QIODevice> data,
- const QString &filename)
+ const QString &filename,
+ const QSharedPointer<QIODevice> data)
{
auto reply = makeUploadRequest(data);
- connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, data, filename]() {
- reply->deleteLater();
-
- int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if (reply == nullptr)
+ return;
- if (status == 0 || status >= 400) {
- emit syncFailed(reply->errorString());
+ connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename, data]() {
+ auto json = getUploadReply(reply);
+ if (json.isEmpty())
return;
- }
- auto data = reply->readAll();
+ auto mime = reply->request().header(QNetworkRequest::ContentTypeHeader).toString();
+ auto size =
+ reply->request().header(QNetworkRequest::ContentLengthHeader).toLongLong();
- if (data.isEmpty())
- return;
+ emit audioUploaded(
+ roomid, filename, json.value("content_uri").toString(), mime, size);
+ });
+}
- auto json = QJsonDocument::fromJson(data);
+void
+MatrixClient::uploadVideo(const QString &roomid,
+ const QString &filename,
+ const QSharedPointer<QIODevice> data)
+{
+ auto reply = makeUploadRequest(data);
- if (!json.isObject()) {
- qDebug() << "Media upload: Response is not a json object.";
- return;
- }
+ if (reply == nullptr)
+ return;
- QJsonObject object = json.object();
- if (!object.contains("content_uri")) {
- qDebug() << "Media upload: Missing content_uri key";
- qDebug() << object;
+ connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename, data]() {
+ auto json = getUploadReply(reply);
+ if (json.isEmpty())
return;
- }
- emit audioUploaded(roomid, filename, object.value("content_uri").toString());
+ auto mime = reply->request().header(QNetworkRequest::ContentTypeHeader).toString();
+ auto size =
+ reply->request().header(QNetworkRequest::ContentLengthHeader).toLongLong();
+
+ emit videoUploaded(
+ roomid, filename, json.value("content_uri").toString(), mime, size);
});
}
@@ -1227,3 +1198,39 @@ MatrixClient::makeUploadRequest(QSharedPointer<QIODevice> iodev)
return reply;
}
+
+QJsonObject
+MatrixClient::getUploadReply(QNetworkReply *reply)
+{
+ QJsonObject object;
+
+ reply->deleteLater();
+
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (status == 0 || status >= 400) {
+ emit syncFailed(reply->errorString());
+ return object;
+ }
+
+ auto res_data = reply->readAll();
+
+ if (res_data.isEmpty())
+ return object;
+
+ auto json = QJsonDocument::fromJson(res_data);
+
+ if (!json.isObject()) {
+ qDebug() << "Media upload: Response is not a json object.";
+ return object;
+ }
+
+ object = json.object();
+ if (!object.contains("content_uri")) {
+ qDebug() << "Media upload: Missing content_uri key";
+ qDebug() << object;
+ return QJsonObject{};
+ }
+
+ return object;
+}
diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc
index 239f9d54..4927d195 100644
--- a/src/TextInputWidget.cc
+++ b/src/TextInputWidget.cc
@@ -58,9 +58,9 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
connect(typingTimer_, &QTimer::timeout, this, &FilteredTextEdit::stopTyping);
connect(&previewDialog_,
- &dialogs::PreviewImageOverlay::confirmImageUpload,
+ &dialogs::PreviewUploadOverlay::confirmUpload,
this,
- &FilteredTextEdit::receiveImage);
+ &FilteredTextEdit::uploadData);
previewDialog_.hide();
}
@@ -135,28 +135,65 @@ FilteredTextEdit::canInsertFromMimeData(const QMimeData *source) const
void
FilteredTextEdit::insertFromMimeData(const QMimeData *source)
{
- if (source->hasImage()) {
- const auto formats = source->formats();
- const auto idx = formats.indexOf(
- QRegularExpression{"image/.+", QRegularExpression::CaseInsensitiveOption});
+ const auto formats = source->formats().filter("/");
+ const auto image = formats.filter("image/", Qt::CaseInsensitive);
+ const auto audio = formats.filter("audio/", Qt::CaseInsensitive);
+ const auto video = formats.filter("video/", Qt::CaseInsensitive);
- // Note: in the future we may want to look into what the best choice is from the
- // formats list. For now we will default to PNG format.
- QString type = "png";
- if (idx != -1) {
- type = formats.at(idx).split('/')[1];
+ if (!image.empty()) {
+ showPreview(source, image);
+ } else if (!audio.empty()) {
+ showPreview(source, audio);
+ } else if (!video.empty()) {
+ showPreview(source, video);
+ } else if (source->hasUrls()) {
+ // Generic file path for any platform.
+ QString path;
+ for (auto &&u : source->urls()) {
+ if (u.isLocalFile()) {
+ path = u.toLocalFile();
+ break;
+ }
}
- // Encode raw pixel data of image.
- QByteArray data = source->data("image/" + type);
- previewDialog_.setImageAndCreate(data, type);
- previewDialog_.show();
- } else if (source->hasFormat("x-special/gnome-copied-files") &&
- QImageReader{source->text()}.canRead()) {
+ if (!path.isEmpty() && QFileInfo{path}.exists()) {
+ previewDialog_.setPreview(path);
+ } else {
+ qWarning()
+ << "Clipboard does not contain any valid file paths:" << source->urls();
+ }
+ } else if (source->hasFormat("x-special/gnome-copied-files")) {
// Special case for X11 users. See "Notes for X11 Users" in source.
// Source: http://doc.qt.io/qt-5/qclipboard.html
- previewDialog_.setImageAndCreate(source->text());
- previewDialog_.show();
+
+ // This MIME type returns a string with multiple lines separated by '\n'. The first
+ // line is the command to perform with the clipboard (not useful to us). The
+ // following lines are the file URIs.
+ //
+ // Source: the nautilus source code in file 'src/nautilus-clipboard.c' in function
+ // nautilus_clipboard_get_uri_list_from_selection_data()
+ // https://github.com/GNOME/nautilus/blob/master/src/nautilus-clipboard.c
+
+ auto data = source->data("x-special/gnome-copied-files").split('\n');
+ if (data.size() < 2) {
+ qWarning() << "MIME format is malformed, cannot perform paste.";
+ return;
+ }
+
+ QString path;
+ for (int i = 1; i < data.size(); ++i) {
+ QUrl url{data[i]};
+ if (url.isLocalFile()) {
+ path = url.toLocalFile();
+ break;
+ }
+ }
+
+ if (!path.isEmpty()) {
+ previewDialog_.setPreview(path);
+ } else {
+ qWarning() << "Clipboard does not contain any valid file paths:" << data;
+ }
} else {
QTextEdit::insertFromMimeData(source);
}
@@ -233,11 +270,30 @@ FilteredTextEdit::textChanged()
}
void
-FilteredTextEdit::receiveImage(const QByteArray img, const QString &img_name)
+FilteredTextEdit::uploadData(const QByteArray data, const QString &media, const QString &filename)
{
QSharedPointer<QBuffer> buffer{new QBuffer{this}};
- buffer->setData(img);
- emit image(buffer, img_name);
+ buffer->setData(data);
+
+ emit startedUpload();
+
+ if (media == "image")
+ emit image(buffer, filename);
+ else if (media == "audio")
+ emit audio(buffer, filename);
+ else if (media == "video")
+ emit video(buffer, filename);
+ else
+ emit file(buffer, filename);
+}
+
+void
+FilteredTextEdit::showPreview(const QMimeData *source, const QStringList &formats)
+{
+ // Retrieve data as MIME type.
+ auto const &mime = formats.first();
+ QByteArray data = source->data(mime);
+ previewDialog_.setPreview(data, mime);
}
TextInputWidget::TextInputWidget(QWidget *parent)
@@ -309,6 +365,9 @@ TextInputWidget::TextInputWidget(QWidget *parent)
connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage);
connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command);
connect(input_, &FilteredTextEdit::image, this, &TextInputWidget::uploadImage);
+ connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio);
+ connect(input_, &FilteredTextEdit::video, this, &TextInputWidget::uploadVideo);
+ connect(input_, &FilteredTextEdit::file, this, &TextInputWidget::uploadFile);
connect(emojiBtn_,
SIGNAL(emojiSelected(const QString &)),
this,
@@ -317,6 +376,9 @@ TextInputWidget::TextInputWidget(QWidget *parent)
connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping);
connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping);
+
+ connect(
+ input_, &FilteredTextEdit::startedUpload, this, &TextInputWidget::showUploadSpinner);
}
void
@@ -376,6 +438,8 @@ TextInputWidget::openFileSelection()
emit uploadImage(file, fileName);
else if (format == "audio")
emit uploadAudio(file, fileName);
+ else if (format == "video")
+ emit uploadVideo(file, fileName);
else
emit uploadFile(file, fileName);
diff --git a/src/Utils.cc b/src/Utils.cc
index 01f0b67e..858d4e76 100644
--- a/src/Utils.cc
+++ b/src/Utils.cc
@@ -133,3 +133,19 @@ utils::firstChar(const QString &input)
return QString::fromUcs4(&input.toUcs4().at(0), 1).toUpper();
}
+
+QString
+utils::humanReadableFileSize(const uint64_t bytes)
+{
+ constexpr static const char *units[] = {"B", "KiB", "MiB", "GiB", "TiB"};
+ constexpr static const int length = sizeof(units) / sizeof(units[0]);
+
+ int u = 0;
+ double size = static_cast<double>(bytes);
+ while (size >= 1024.0 && u < length) {
+ ++u;
+ size /= 1024.0;
+ }
+
+ return QString::number(size, 'g', 4) + ' ' + units[u];
+}
diff --git a/src/dialogs/PreviewImageOverlay.cc b/src/dialogs/PreviewImageOverlay.cc
deleted file mode 100644
index 31ef00ed..00000000
--- a/src/dialogs/PreviewImageOverlay.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <QApplication>
-#include <QBuffer>
-#include <QDebug>
-#include <QFile>
-#include <QFileInfo>
-#include <QHBoxLayout>
-#include <QVBoxLayout>
-
-#include "Config.h"
-
-#include "dialogs/PreviewImageOverlay.h"
-
-using namespace dialogs;
-
-static constexpr const char *DEFAULT = "Upload image?";
-static constexpr const char *ERROR = "Failed to load image type '%1'. Continue upload?";
-
-PreviewImageOverlay::PreviewImageOverlay(QWidget *parent)
- : QWidget{parent}
- , titleLabel_{tr(DEFAULT), this}
- , imageLabel_{this}
- , imageName_{tr("clipboard"), this}
- , upload_{tr("Upload"), this}
- , cancel_{tr("Cancel"), this}
-{
- auto hlayout = new QHBoxLayout;
- hlayout->addWidget(&upload_);
- hlayout->addWidget(&cancel_);
-
- auto vlayout = new QVBoxLayout{this};
- vlayout->addWidget(&titleLabel_);
- vlayout->addWidget(&imageLabel_);
- vlayout->addWidget(&imageName_);
- vlayout->addLayout(hlayout);
-
- connect(&upload_, &QPushButton::clicked, [&]() {
- emit confirmImageUpload(imageData_, imageName_.text());
- close();
- });
- connect(&cancel_, &QPushButton::clicked, [&]() { close(); });
-}
-
-void
-PreviewImageOverlay::init()
-{
- auto window = QApplication::activeWindow();
- auto winsize = window->frameGeometry().size();
- auto center = window->frameGeometry().center();
- auto img_size = image_.size();
-
- imageName_.setText(QFileInfo{imagePath_}.fileName());
-
- setAutoFillBackground(true);
- setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
- setWindowModality(Qt::WindowModal);
-
- titleLabel_.setStyleSheet(
- QString{"font-weight: bold; font-size: %1px;"}.arg(conf::headerFontSize));
- titleLabel_.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- titleLabel_.setAlignment(Qt::AlignCenter);
- imageLabel_.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
- imageLabel_.setAlignment(Qt::AlignCenter);
- imageName_.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
- imageName_.setAlignment(Qt::AlignCenter);
- upload_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- cancel_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- upload_.setFontSize(conf::btn::fontSize);
- cancel_.setFontSize(conf::btn::fontSize);
-
- // Scale image preview to the size of the current window if it is larger.
- if ((img_size.height() * img_size.width()) > (winsize.height() * winsize.width())) {
- imageLabel_.setPixmap(image_.scaled(winsize, Qt::KeepAspectRatio));
- } else {
- imageLabel_.setPixmap(image_);
- move(center.x() - (width() * 0.5), center.y() - (height() * 0.5));
- }
- imageLabel_.setScaledContents(false);
-
- raise();
-}
-
-void
-PreviewImageOverlay::setImageAndCreate(const QByteArray data, const QString &type)
-{
- imageData_ = data;
- imagePath_ = "clipboard." + type;
- auto loaded = image_.loadFromData(imageData_);
- if (!loaded) {
- titleLabel_.setText(QString{tr(ERROR)}.arg(type));
- } else {
- titleLabel_.setText(tr(DEFAULT));
- }
-
- init();
-}
-
-void
-PreviewImageOverlay::setImageAndCreate(const QString &path)
-{
- QFile file{path};
- imagePath_ = path;
-
- if (!file.open(QIODevice::ReadOnly)) {
- qWarning() << "Failed to open image from:" << path;
- qWarning() << "Reason:" << file.errorString();
- close();
- return;
- }
-
- if ((imageData_ = file.readAll()).isEmpty()) {
- qWarning() << "Failed to read image:" << file.errorString();
- close();
- return;
- }
-
- auto loaded = image_.loadFromData(imageData_);
- if (!loaded) {
- auto t = QFileInfo{path}.suffix();
- titleLabel_.setText(QString{tr(ERROR)}.arg(t));
- } else {
- titleLabel_.setText(tr(DEFAULT));
- }
-
- init();
-}
diff --git a/src/dialogs/PreviewUploadOverlay.cc b/src/dialogs/PreviewUploadOverlay.cc
new file mode 100644
index 00000000..f2007011
--- /dev/null
+++ b/src/dialogs/PreviewUploadOverlay.cc
@@ -0,0 +1,170 @@
+/*
+ * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QApplication>
+#include <QBuffer>
+#include <QDebug>
+#include <QFile>
+#include <QFileInfo>
+#include <QHBoxLayout>
+#include <QMimeDatabase>
+#include <QVBoxLayout>
+
+#include "Config.h"
+#include "Utils.h"
+
+#include "dialogs/PreviewUploadOverlay.h"
+
+using namespace dialogs;
+
+static constexpr const char *DEFAULT = "Upload %1?";
+static constexpr const char *ERROR = "Failed to load image type '%1'. Continue upload?";
+
+PreviewUploadOverlay::PreviewUploadOverlay(QWidget *parent)
+ : QWidget{parent}
+ , titleLabel_{this}
+ , fileName_{this}
+ , upload_{tr("Upload"), this}
+ , cancel_{tr("Cancel"), this}
+{
+ auto hlayout = new QHBoxLayout;
+ hlayout->addWidget(&upload_);
+ hlayout->addWidget(&cancel_);
+
+ auto vlayout = new QVBoxLayout{this};
+ vlayout->addWidget(&titleLabel_);
+ vlayout->addWidget(&infoLabel_);
+ vlayout->addWidget(&fileName_);
+ vlayout->addLayout(hlayout);
+
+ connect(&upload_, &QPushButton::clicked, [&]() {
+ emit confirmUpload(data_, mediaType_, fileName_.text());
+ close();
+ });
+ connect(&cancel_, &QPushButton::clicked, this, &PreviewUploadOverlay::close);
+}
+
+void
+PreviewUploadOverlay::init()
+{
+ auto window = QApplication::activeWindow();
+ auto winsize = window->frameGeometry().size();
+ auto center = window->frameGeometry().center();
+ auto img_size = image_.size();
+
+ fileName_.setText(QFileInfo{filePath_}.fileName());
+
+ setAutoFillBackground(true);
+ setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
+ setWindowModality(Qt::WindowModal);
+
+ titleLabel_.setStyleSheet(
+ QString{"font-weight: bold; font-size: %1px;"}.arg(conf::headerFontSize));
+ titleLabel_.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ titleLabel_.setAlignment(Qt::AlignCenter);
+ infoLabel_.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+ fileName_.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ fileName_.setAlignment(Qt::AlignCenter);
+ upload_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ cancel_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ upload_.setFontSize(conf::btn::fontSize);
+ cancel_.setFontSize(conf::btn::fontSize);
+
+ if (isImage_) {
+ infoLabel_.setAlignment(Qt::AlignCenter);
+
+ // Scale image preview to the size of the current window if it is larger.
+ if ((img_size.height() * img_size.width()) > (winsize.height() * winsize.width())) {
+ infoLabel_.setPixmap(image_.scaled(winsize, Qt::KeepAspectRatio));
+ } else {
+ infoLabel_.setPixmap(image_);
+ move(center.x() - (width() * 0.5), center.y() - (height() * 0.5));
+ }
+ } else {
+ infoLabel_.setAlignment(Qt::AlignLeft);
+ }
+ infoLabel_.setScaledContents(false);
+
+ show();
+}
+
+void
+PreviewUploadOverlay::setLabels(const QString &type, const QString &mime, const int upload_size)
+{
+ if (mediaType_ == "image") {
+ if (!image_.loadFromData(data_)) {
+ titleLabel_.setText(QString{tr(ERROR)}.arg(type));
+ } else {
+ titleLabel_.setText(QString{tr(DEFAULT)}.arg(mediaType_));
+ }
+ isImage_ = true;
+ } else {
+ auto const info = QString{tr("Media type: %1\n"
+ "Media size: %2\n")}
+ .arg(mime)
+ .arg(utils::humanReadableFileSize(upload_size));
+
+ titleLabel_.setText(QString{tr(DEFAULT)}.arg("file"));
+ infoLabel_.setText(info);
+ }
+}
+
+void
+PreviewUploadOverlay::setPreview(const QByteArray data, const QString &mime)
+{
+ auto const &split = mime.split('/');
+ auto const &type = split[1];
+
+ data_ = data;
+ mediaType_ = split[0];
+ filePath_ = "clipboard." + type;
+ isImage_ = false;
+
+ setLabels(type, mime, data_.size());
+ init();
+}
+
+void
+PreviewUploadOverlay::setPreview(const QString &path)
+{
+ QFile file{path};
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to open file from:" << path;
+ qWarning() << "Reason:" << file.errorString();
+ close();
+ return;
+ }
+
+ QMimeDatabase db;
+ auto mime = db.mimeTypeForFileNameAndData(path, &file);
+
+ if ((data_ = file.readAll()).isEmpty()) {
+ qWarning() << "Failed to read media:" << file.errorString();
+ close();
+ return;
+ }
+
+ auto const &split = mime.name().split('/');
+
+ mediaType_ = split[0];
+ filePath_ = file.fileName();
+ isImage_ = false;
+
+ setLabels(split[1], mime.name(), data_.size());
+ init();
+}
diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc
index 8d1c8ae1..82f22d1f 100644
--- a/src/timeline/TimelineView.cc
+++ b/src/timeline/TimelineView.cc
@@ -515,7 +515,7 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
lastSender_ = local_user_;
int txn_id = client_->incrementTransactionId();
- PendingMessage message(ty, txn_id, body, "", "", view_item);
+ PendingMessage message(ty, txn_id, body, "", "", -1, "", view_item);
handleNewUserMessage(message);
}
@@ -537,13 +537,14 @@ TimelineView::sendNextPendingMessage()
switch (m.ty) {
case mtx::events::MessageType::Audio:
case mtx::events::MessageType::Image:
+ case mtx::events::MessageType::Video:
case mtx::events::MessageType::File:
// FIXME: Improve the API
client_->sendRoomMessage(
- m.ty, m.txn_id, room_id_, m.filename, QFileInfo(m.filename), m.body);
+ m.ty, m.txn_id, room_id_, m.filename, m.mime, m.media_size, m.body);
break;
default:
- client_->sendRoomMessage(m.ty, m.txn_id, room_id_, m.body, QFileInfo());
+ client_->sendRoomMessage(m.ty, m.txn_id, room_id_, m.body, m.mime, m.media_size);
break;
}
}
diff --git a/src/timeline/TimelineViewManager.cc b/src/timeline/TimelineViewManager.cc
index 7bee8869..0e2bde2e 100644
--- a/src/timeline/TimelineViewManager.cc
+++ b/src/timeline/TimelineViewManager.cc
@@ -29,6 +29,7 @@
#include "timeline/widgets/AudioItem.h"
#include "timeline/widgets/FileItem.h"
#include "timeline/widgets/ImageItem.h"
+#include "timeline/widgets/VideoItem.h"
TimelineViewManager::TimelineViewManager(QSharedPointer<MatrixClient> client, QWidget *parent)
: QStackedWidget(parent)
@@ -89,9 +90,10 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
void
TimelineViewManager::queueImageMessage(const QString &roomid,
- const QSharedPointer<QIODevice> data,
const QString &filename,
- const QString &url)
+ const QString &url,
+ const QString &mime,
+ const int64_t size)
{
if (!timelineViewExists(roomid)) {
qDebug() << "Cannot send m.image message to a non-managed view";
@@ -100,13 +102,15 @@ TimelineViewManager::queueImageMessage(const QString &roomid,
auto view = views_[roomid];
- view->addUserMessage<ImageItem, mtx::events::MessageType::Image>(url, filename, data);
+ view->addUserMessage<ImageItem, mtx::events::MessageType::Image>(url, filename, mime, size);
}
void
TimelineViewManager::queueFileMessage(const QString &roomid,
const QString &filename,
- const QString &url)
+ const QString &url,
+ const QString &mime,
+ const int64_t size)
{
if (!timelineViewExists(roomid)) {
qDebug() << "Cannot send m.file message to a non-managed view";
@@ -115,13 +119,15 @@ TimelineViewManager::queueFileMessage(const QString &roomid,
auto view = views_[roomid];
- view->addUserMessage<FileItem, mtx::events::MessageType::File>(url, filename);
+ view->addUserMessage<FileItem, mtx::events::MessageType::File>(url, filename, mime, size);
}
void
TimelineViewManager::queueAudioMessage(const QString &roomid,
const QString &filename,
- const QString &url)
+ const QString &url,
+ const QString &mime,
+ const int64_t size)
{
if (!timelineViewExists(roomid)) {
qDebug() << "Cannot send m.audio message to a non-managed view";
@@ -130,7 +136,24 @@ TimelineViewManager::queueAudioMessage(const QString &roomid,
auto view = views_[roomid];
- view->addUserMessage<AudioItem, mtx::events::MessageType::Audio>(url, filename);
+ view->addUserMessage<AudioItem, mtx::events::MessageType::Audio>(url, filename, mime, size);
+}
+
+void
+TimelineViewManager::queueVideoMessage(const QString &roomid,
+ const QString &filename,
+ const QString &url,
+ const QString &mime,
+ const int64_t size)
+{
+ if (!timelineViewExists(roomid)) {
+ qDebug() << "Cannot send m.video message to a non-managed view";
+ return;
+ }
+
+ auto view = views_[roomid];
+
+ view->addUserMessage<VideoItem, mtx::events::MessageType::Video>(url, filename, mime, size);
}
void
diff --git a/src/timeline/widgets/AudioItem.cc b/src/timeline/widgets/AudioItem.cc
index e84cbb3a..9075bc55 100644
--- a/src/timeline/widgets/AudioItem.cc
+++ b/src/timeline/widgets/AudioItem.cc
@@ -20,10 +20,11 @@
#include <QDesktopServices>
#include <QFile>
#include <QFileDialog>
-#include <QFileInfo>
#include <QPainter>
#include <QPixmap>
+#include "Utils.h"
+
#include "timeline/widgets/AudioItem.h"
constexpr int MaxWidth = 400;
@@ -82,42 +83,26 @@ AudioItem::AudioItem(QSharedPointer<MatrixClient> client,
, event_{event}
, client_{client}
{
- readableFileSize_ = calculateFileSize(event.content.info.size);
+ readableFileSize_ = utils::humanReadableFileSize(event.content.info.size);
init();
}
AudioItem::AudioItem(QSharedPointer<MatrixClient> client,
const QString &url,
- const QSharedPointer<QIODevice> data,
const QString &filename,
+ const int64_t size,
QWidget *parent)
: QWidget(parent)
, url_{url}
- , text_{QFileInfo{filename}.fileName()}
+ , text_{filename}
, client_{client}
{
- Q_UNUSED(data);
- readableFileSize_ = calculateFileSize(QFileInfo{filename}.size());
+ readableFileSize_ = utils::humanReadableFileSize(size);
init();
}
-QString
-AudioItem::calculateFileSize(int nbytes) const
-{
- if (nbytes == 0)
- return QString("");
-
- if (nbytes < 1024)
- return QString("%1 B").arg(nbytes);
-
- if (nbytes < 1024 * 1024)
- return QString("%1 KB").arg(nbytes / 1024);
-
- return QString("%1 MB").arg(nbytes / 1024 / 1024);
-}
-
QSize
AudioItem::sizeHint() const
{
diff --git a/src/timeline/widgets/FileItem.cc b/src/timeline/widgets/FileItem.cc
index a6159309..eda6e835 100644
--- a/src/timeline/widgets/FileItem.cc
+++ b/src/timeline/widgets/FileItem.cc
@@ -20,10 +20,11 @@
#include <QDesktopServices>
#include <QFile>
#include <QFileDialog>
-#include <QFileInfo>
#include <QPainter>
#include <QPixmap>
+#include "Utils.h"
+
#include "timeline/widgets/FileItem.h"
constexpr int MaxWidth = 400;
@@ -69,42 +70,26 @@ FileItem::FileItem(QSharedPointer<MatrixClient> client,
, event_{event}
, client_{client}
{
- readableFileSize_ = calculateFileSize(event.content.info.size);
+ readableFileSize_ = utils::humanReadableFileSize(event.content.info.size);
init();
}
FileItem::FileItem(QSharedPointer<MatrixClient> client,
const QString &url,
- const QSharedPointer<QIODevice> data,
const QString &filename,
+ const int64_t size,
QWidget *parent)
: QWidget(parent)
, url_{url}
- , text_{QFileInfo{filename}.fileName()}
+ , text_{filename}
, client_{client}
{
- Q_UNUSED(data);
- readableFileSize_ = calculateFileSize(QFileInfo{filename}.size());
+ readableFileSize_ = utils::humanReadableFileSize(size);
init();
}
-QString
-FileItem::calculateFileSize(int nbytes) const
-{
- if (nbytes == 0)
- return QString("");
-
- if (nbytes < 1024)
- return QString("%1 B").arg(nbytes);
-
- if (nbytes < 1024 * 1024)
- return QString("%1 KB").arg(nbytes / 1024);
-
- return QString("%1 MB").arg(nbytes / 1024 / 1024);
-}
-
void
FileItem::openUrl()
{
diff --git a/src/timeline/widgets/ImageItem.cc b/src/timeline/widgets/ImageItem.cc
index f713989e..f91799c3 100644
--- a/src/timeline/widgets/ImageItem.cc
+++ b/src/timeline/widgets/ImageItem.cc
@@ -61,14 +61,16 @@ ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
const QString &url,
- const QSharedPointer<QIODevice> data,
const QString &filename,
+ const int64_t size,
QWidget *parent)
: QWidget(parent)
, url_{url}
, text_{filename}
, client_{client}
{
+ Q_UNUSED(size);
+
setMouseTracking(true);
setCursor(Qt::PointingHandCursor);
setAttribute(Qt::WA_Hover, true);
@@ -84,19 +86,12 @@ ImageItem::ImageItem(QSharedPointer<MatrixClient> client,
url_ = QString("%1/_matrix/media/r0/download/%2")
.arg(client_.data()->getHomeServer().toString(), media_params);
- if (data.isNull()) {
- qWarning() << "No image data to display";
- return;
- }
+ client_.data()->downloadImage(QString::fromStdString(event_.event_id), url_);
- if (data->reset()) {
- QPixmap p;
- p.loadFromData(data->readAll());
- setImage(p);
- } else {
- qWarning() << "Failed to seek to beginning of device:" << data->errorString();
- return;
- }
+ connect(client_.data(),
+ SIGNAL(imageDownloaded(const QString &, const QPixmap &)),
+ this,
+ SLOT(imageDownloaded(const QString &, const QPixmap &)));
}
void
diff --git a/src/timeline/widgets/VideoItem.cc b/src/timeline/widgets/VideoItem.cc
index b46dff7b..34c0a643 100644
--- a/src/timeline/widgets/VideoItem.cc
+++ b/src/timeline/widgets/VideoItem.cc
@@ -20,6 +20,7 @@
#include <QVBoxLayout>
#include "Config.h"
+#include "Utils.h"
#include "timeline/widgets/VideoItem.h"
void
@@ -45,7 +46,7 @@ VideoItem::VideoItem(QSharedPointer<MatrixClient> client,
, event_{event}
, client_{client}
{
- readableFileSize_ = calculateFileSize(event.content.info.size);
+ readableFileSize_ = utils::humanReadableFileSize(event.content.info.size);
init();
@@ -66,31 +67,15 @@ VideoItem::VideoItem(QSharedPointer<MatrixClient> client,
VideoItem::VideoItem(QSharedPointer<MatrixClient> client,
const QString &url,
- const QSharedPointer<QIODevice> data,
const QString &filename,
+ const int64_t size,
QWidget *parent)
: QWidget(parent)
, url_{url}
- , text_{QFileInfo(filename).fileName()}
+ , text_{filename}
, client_{client}
{
- Q_UNUSED(data);
- readableFileSize_ = calculateFileSize(QFileInfo(filename).size());
+ readableFileSize_ = utils::humanReadableFileSize(size);
init();
}
-
-QString
-VideoItem::calculateFileSize(int nbytes) const
-{
- if (nbytes == 0)
- return QString("");
-
- if (nbytes < 1024)
- return QString("%1 B").arg(nbytes);
-
- if (nbytes < 1024 * 1024)
- return QString("%1 KB").arg(nbytes / 1024);
-
- return QString("%1 MB").arg(nbytes / 1024 / 1024);
-}
|