diff --git a/CMakeLists.txt b/CMakeLists.txt index 691c2171..7c1355de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,7 +145,7 @@ set(SRC_FILES # Dialogs src/dialogs/CreateRoom.cc src/dialogs/ImageOverlay.cc - src/dialogs/PreviewImageOverlay.cc + src/dialogs/PreviewUploadOverlay.cc src/dialogs/InviteUsers.cc src/dialogs/JoinRoom.cc src/dialogs/LeaveRoom.cc @@ -231,7 +231,7 @@ qt5_wrap_cpp(MOC_HEADERS # Dialogs include/dialogs/CreateRoom.h include/dialogs/ImageOverlay.h - include/dialogs/PreviewImageOverlay.h + include/dialogs/PreviewUploadOverlay.h include/dialogs/InviteUsers.h include/dialogs/JoinRoom.h include/dialogs/LeaveRoom.h diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 3efd2d0a..d9f5067a 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -39,7 +39,8 @@ public: int txnId, const QString &roomid, const QString &msg, - const QFileInfo &info, + const QString &mime, + const int64_t media_size, const QString &url = "") noexcept; void login(const QString &username, const QString &password) noexcept; void registerUser(const QString &username, @@ -58,14 +59,17 @@ public: void downloadFile(const QString &event_id, const QUrl &url); void messages(const QString &room_id, const QString &from_token, int limit = 30) noexcept; void uploadImage(const QString &roomid, - const QSharedPointer data, - const QString &filename); + const QString &filename, + const QSharedPointer data); void uploadFile(const QString &roomid, - const QSharedPointer data, - const QString &filename); + const QString &filename, + const QSharedPointer data); void uploadAudio(const QString &roomid, - const QSharedPointer data, - const QString &filename); + const QString &filename, + const QSharedPointer data); + void uploadVideo(const QString &roomid, + const QString &filename, + const QSharedPointer data); void uploadFilter(const QString &filter) noexcept; void joinRoom(const QString &roomIdOrAlias); void leaveRoom(const QString &roomId); @@ -108,12 +112,25 @@ signals: const QString &token); void versionSuccess(); void imageUploaded(const QString &roomid, - const QSharedPointer data, const QString &filename, - const QString &url); - void fileUploaded(const QString &roomid, const QString &filename, const QString &url); - void audioUploaded(const QString &roomid, const QString &filename, const QString &url); - + const QString &url, + const QString &mime, + const int64_t size); + void fileUploaded(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + const int64_t size); + void audioUploaded(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + const int64_t size); + void videoUploaded(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + const int64_t size); void roomAvatarRetrieved(const QString &roomid, const QPixmap &img, const QString &url, @@ -143,6 +160,7 @@ signals: private: QNetworkReply *makeUploadRequest(QSharedPointer iodev); + QJsonObject getUploadReply(QNetworkReply *reply); // Client API prefix. QString clientApiUrl_; diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index 6d28009e..b89b7753 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -27,12 +27,12 @@ #include "FlatButton.h" #include "LoadingIndicator.h" -#include "dialogs/PreviewImageOverlay.h" +#include "dialogs/PreviewUploadOverlay.h" #include "emoji/PickButton.h" namespace dialogs { -class PreviewImageOverlay; +class PreviewUploadOverlay; } class FilteredTextEdit : public QTextEdit @@ -53,9 +53,13 @@ signals: void heightChanged(int height); void startedTyping(); void stoppedTyping(); + void startedUpload(); void message(QString); void command(QString name, QString args); - void image(const QSharedPointer iodev, const QString &img_name); + void image(QSharedPointer data, const QString &filename); + void audio(QSharedPointer data, const QString &filename); + void video(QSharedPointer data, const QString &filename); + void file(QSharedPointer data, const QString &filename); protected: void keyPressEvent(QKeyEvent *event) override; @@ -67,11 +71,12 @@ private: size_t history_index_; QTimer *typingTimer_; - dialogs::PreviewImageOverlay previewDialog_; + dialogs::PreviewUploadOverlay previewDialog_; void textChanged(); - void receiveImage(const QByteArray img, const QString &img_name); + void uploadData(const QByteArray data, const QString &media, const QString &filename); void afterCompletion(int); + void showPreview(const QMimeData *source, const QStringList &formats); }; class TextInputWidget : public QWidget @@ -95,9 +100,10 @@ signals: void sendTextMessage(QString msg); void sendEmoteMessage(QString msg); - void uploadImage(QSharedPointer data, const QString &filename); - void uploadFile(QSharedPointer data, const QString &filename); - void uploadAudio(QSharedPointer data, const QString &filename); + void uploadImage(const QSharedPointer data, const QString &filename); + void uploadFile(const QSharedPointer data, const QString &filename); + void uploadAudio(const QSharedPointer data, const QString &filename); + void uploadVideo(const QSharedPointer data, const QString &filename); void sendJoinRoomRequest(const QString &room); diff --git a/include/Utils.h b/include/Utils.h index bbe46dd8..2c764f88 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -22,4 +22,8 @@ getMessageDescription(const TimelineEvent &event, const QString &localUser); //! surrogate pairs might be in use. QString firstChar(const QString &input); + +//! Get a human readable file size with the appropriate units attached. +QString +humanReadableFileSize(const uint64_t bytes); } diff --git a/include/dialogs/PreviewImageOverlay.h b/include/dialogs/PreviewUploadOverlay.h similarity index 65% rename from include/dialogs/PreviewImageOverlay.h rename to include/dialogs/PreviewUploadOverlay.h index a1ab32ee..c9516719 100644 --- a/include/dialogs/PreviewImageOverlay.h +++ b/include/dialogs/PreviewUploadOverlay.h @@ -28,28 +28,32 @@ class QMimeData; namespace dialogs { -class PreviewImageOverlay : public QWidget +class PreviewUploadOverlay : public QWidget { Q_OBJECT public: - PreviewImageOverlay(QWidget *parent = nullptr); + PreviewUploadOverlay(QWidget *parent = nullptr); - void setImageAndCreate(const QByteArray data, const QString &type); - void setImageAndCreate(const QString &path); + void setPreview(const QByteArray data, const QString &mime); + void setPreview(const QString &path); signals: - void confirmImageUpload(const QByteArray data, const QString &img_name); + void confirmUpload(const QByteArray data, const QString &media, const QString &filename); private: void init(); + void setLabels(const QString &type, const QString &mime, const int upload_size); + bool isImage_; QPixmap image_; - QByteArray imageData_; - QString imagePath_; + + QByteArray data_; + QString filePath_; + QString mediaType_; QLabel titleLabel_; - QLabel imageLabel_; - QLineEdit imageName_; + QLabel infoLabel_; + QLineEdit fileName_; FlatButton upload_; FlatButton cancel_; diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h index 6e1287c8..782f3afd 100644 --- a/include/timeline/TimelineView.h +++ b/include/timeline/TimelineView.h @@ -44,6 +44,8 @@ struct PendingMessage int txn_id; QString body; QString filename; + QString mime; + int64_t media_size; QString event_id; TimelineItem *widget; @@ -51,12 +53,16 @@ struct PendingMessage int txn_id, QString body, QString filename, + QString mime, + int64_t media_size, QString event_id, TimelineItem *widget) : ty(ty) , txn_id(txn_id) , body(body) , filename(filename) + , mime(mime) + , media_size(media_size) , event_id(event_id) , widget(widget) {} @@ -87,10 +93,10 @@ public: void addUserMessage(mtx::events::MessageType ty, const QString &msg); template - void addUserMessage( - const QString &url, - const QString &filename, - const QSharedPointer data = QSharedPointer(nullptr)); + void addUserMessage(const QString &url, + const QString &filename, + const QString &mime, + const int64_t size); void updatePendingMessage(int txn_id, QString event_id); void scrollDown(); QLabel *createDateSeparator(QDateTime datetime); @@ -236,11 +242,13 @@ template void TimelineView::addUserMessage(const QString &url, const QString &filename, - const QSharedPointer data) + const QString &mime, + const int64_t size) { auto with_sender = lastSender_ != local_user_; + auto trimmed = QFileInfo{filename}.fileName(); // Trim file path. - auto widget = new Widget(client_, url, data, filename, this); + auto widget = new Widget(client_, url, trimmed, size, this); TimelineItem *view_item = new TimelineItem(widget, local_user_, with_sender, scroll_widget_); @@ -255,7 +263,7 @@ TimelineView::addUserMessage(const QString &url, int txn_id = client_->incrementTransactionId(); - PendingMessage message(MsgType, txn_id, url, filename, "", view_item); + PendingMessage message(MsgType, txn_id, url, trimmed, mime, size, "", view_item); handleNewUserMessage(message); } diff --git a/include/timeline/TimelineViewManager.h b/include/timeline/TimelineViewManager.h index 74d8588c..a531be88 100644 --- a/include/timeline/TimelineViewManager.h +++ b/include/timeline/TimelineViewManager.h @@ -64,11 +64,25 @@ public slots: void queueTextMessage(const QString &msg); void queueEmoteMessage(const QString &msg); void queueImageMessage(const QString &roomid, - const QSharedPointer data, const QString &filename, - const QString &url); - void queueFileMessage(const QString &roomid, const QString &filename, const QString &url); - void queueAudioMessage(const QString &roomid, const QString &filename, const QString &url); + const QString &url, + const QString &mime, + const int64_t dsize); + void queueFileMessage(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + const int64_t dsize); + void queueAudioMessage(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + const int64_t dsize); + void queueVideoMessage(const QString &roomid, + const QString &filename, + const QString &url, + const QString &mime, + const int64_t dsize); private slots: void messageSent(const QString &eventid, const QString &roomid, int txnid); diff --git a/include/timeline/widgets/AudioItem.h b/include/timeline/widgets/AudioItem.h index ca81f498..06f4b8ff 100644 --- a/include/timeline/widgets/AudioItem.h +++ b/include/timeline/widgets/AudioItem.h @@ -48,8 +48,8 @@ public: AudioItem(QSharedPointer client, const QString &url, - const QSharedPointer data, const QString &filename, + const int64_t size, QWidget *parent = nullptr); QSize sizeHint() const override; @@ -76,7 +76,6 @@ private slots: void fileDownloaded(const QString &event_id, const QByteArray &data); private: - QString calculateFileSize(int nbytes) const; void init(); enum class AudioState diff --git a/include/timeline/widgets/FileItem.h b/include/timeline/widgets/FileItem.h index 72589189..36d72f25 100644 --- a/include/timeline/widgets/FileItem.h +++ b/include/timeline/widgets/FileItem.h @@ -42,8 +42,8 @@ public: FileItem(QSharedPointer client, const QString &url, - const QSharedPointer data, const QString &filename, + const int64_t size, QWidget *parent = nullptr); QSize sizeHint() const override; @@ -64,7 +64,6 @@ private slots: void fileDownloaded(const QString &event_id, const QByteArray &data); private: - QString calculateFileSize(int nbytes) const; void openUrl(); void init(); diff --git a/include/timeline/widgets/ImageItem.h b/include/timeline/widgets/ImageItem.h index d24b7239..9e5fa82b 100644 --- a/include/timeline/widgets/ImageItem.h +++ b/include/timeline/widgets/ImageItem.h @@ -36,8 +36,8 @@ public: ImageItem(QSharedPointer client, const QString &url, - const QSharedPointer data, const QString &filename, + const int64_t size, QWidget *parent = nullptr); void setImage(const QPixmap &image); diff --git a/include/timeline/widgets/VideoItem.h b/include/timeline/widgets/VideoItem.h index 53c3e21a..65b87c95 100644 --- a/include/timeline/widgets/VideoItem.h +++ b/include/timeline/widgets/VideoItem.h @@ -37,13 +37,12 @@ public: VideoItem(QSharedPointer client, const QString &url, - const QSharedPointer data, const QString &filename, + const int64_t size, QWidget *parent = nullptr); private: void init(); - QString calculateFileSize(int nbytes) const; QUrl url_; QString text_; diff --git a/resources/styles/nheko-dark.qss b/resources/styles/nheko-dark.qss index 584b2389..a9da770f 100644 --- a/resources/styles/nheko-dark.qss +++ b/resources/styles/nheko-dark.qss @@ -106,7 +106,7 @@ dialogs--CreateRoom, dialogs--InviteUsers, dialogs--ReadReceipts, dialogs--JoinRoom, -dialogs--PreviewImageOverlay { +dialogs--PreviewUploadOverlay { background-color: #383c4a; color: #caccd1; } diff --git a/resources/styles/nheko.qss b/resources/styles/nheko.qss index 1e300494..ec3537b9 100644 --- a/resources/styles/nheko.qss +++ b/resources/styles/nheko.qss @@ -109,7 +109,7 @@ dialogs--CreateRoom, dialogs--InviteUsers, dialogs--ReadReceipts, dialogs--JoinRoom, -dialogs--PreviewImageOverlay, +dialogs--PreviewUploadOverlay, QListWidget { background-color: white; color: #333; 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 client, &TextInputWidget::uploadImage, this, [=](QSharedPointer data, const QString &fn) { - client_->uploadImage(current_room_, data, fn); + client_->uploadImage(current_room_, fn, data); }); connect(text_input_, &TextInputWidget::uploadFile, this, [=](QSharedPointer data, const QString &fn) { - client_->uploadFile(current_room_, data, fn); + client_->uploadFile(current_room_, fn, data); }); connect(text_input_, &TextInputWidget::uploadAudio, this, [=](QSharedPointer data, const QString &fn) { - client_->uploadAudio(current_room_, data, fn); + client_->uploadAudio(current_room_, fn, data); + }); + connect(text_input_, + &TextInputWidget::uploadVideo, + this, + [=](QSharedPointer data, const QString &fn) { + client_->uploadVideo(current_room_, fn, data); }); connect( @@ -253,23 +259,30 @@ ChatPage::ChatPage(QSharedPointer client, connect(client_.data(), &MatrixClient::imageUploaded, this, - [=](QString roomid, QSharedPointer 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); + 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_->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(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 data, - const QString &filename) + const QString &filename, + const QSharedPointer 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); + 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 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 data, - const QString &filename) + const QString &filename, + const QSharedPointer data) { auto reply = makeUploadRequest(data); - connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, data, filename]() { - reply->deleteLater(); + if (reply == nullptr) + return; - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - emit syncFailed(reply->errorString()); - return; - } - - auto data = reply->readAll(); - - 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 data, - const QString &filename) + const QString &filename, + const QSharedPointer data) { auto reply = makeUploadRequest(data); - connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, data, filename]() { - reply->deleteLater(); + if (reply == nullptr) + return; - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - emit syncFailed(reply->errorString()); - return; - } - - auto data = reply->readAll(); - - 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."; + emit audioUploaded( + roomid, filename, json.value("content_uri").toString(), mime, size); + }); +} + +void +MatrixClient::uploadVideo(const QString &roomid, + const QString &filename, + const QSharedPointer data) +{ + auto reply = makeUploadRequest(data); + + if (reply == nullptr) + return; + + connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename, data]() { + auto json = getUploadReply(reply); + if (json.isEmpty()) return; - } - QJsonObject object = json.object(); - if (!object.contains("content_uri")) { - qDebug() << "Media upload: Missing content_uri key"; - qDebug() << object; - return; - } + auto mime = reply->request().header(QNetworkRequest::ContentTypeHeader).toString(); + auto size = + reply->request().header(QNetworkRequest::ContentLengthHeader).toLongLong(); - emit audioUploaded(roomid, filename, object.value("content_uri").toString()); + emit videoUploaded( + roomid, filename, json.value("content_uri").toString(), mime, size); }); } @@ -1227,3 +1198,39 @@ MatrixClient::makeUploadRequest(QSharedPointer 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 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(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 - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -#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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 client, QWidget *parent) : QStackedWidget(parent) @@ -89,9 +90,10 @@ TimelineViewManager::queueEmoteMessage(const QString &msg) void TimelineViewManager::queueImageMessage(const QString &roomid, - const QSharedPointer 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(url, filename, data); + view->addUserMessage(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(url, filename); + view->addUserMessage(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(url, filename); + view->addUserMessage(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(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 #include #include -#include #include #include +#include "Utils.h" + #include "timeline/widgets/AudioItem.h" constexpr int MaxWidth = 400; @@ -82,42 +83,26 @@ AudioItem::AudioItem(QSharedPointer client, , event_{event} , client_{client} { - readableFileSize_ = calculateFileSize(event.content.info.size); + readableFileSize_ = utils::humanReadableFileSize(event.content.info.size); init(); } AudioItem::AudioItem(QSharedPointer client, const QString &url, - const QSharedPointer 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 #include #include -#include #include #include +#include "Utils.h" + #include "timeline/widgets/FileItem.h" constexpr int MaxWidth = 400; @@ -69,42 +70,26 @@ FileItem::FileItem(QSharedPointer client, , event_{event} , client_{client} { - readableFileSize_ = calculateFileSize(event.content.info.size); + readableFileSize_ = utils::humanReadableFileSize(event.content.info.size); init(); } FileItem::FileItem(QSharedPointer client, const QString &url, - const QSharedPointer 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 client, ImageItem::ImageItem(QSharedPointer client, const QString &url, - const QSharedPointer 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 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 #include "Config.h" +#include "Utils.h" #include "timeline/widgets/VideoItem.h" void @@ -45,7 +46,7 @@ VideoItem::VideoItem(QSharedPointer 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 client, VideoItem::VideoItem(QSharedPointer client, const QString &url, - const QSharedPointer 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); -}