diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 859d24d020..38ac04e215 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -7600,4 +7600,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mac_hold_to_quit" = "Hold {text} to Quit"; +"lng_rename_file" = "Rename file"; + // Keys finished diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index af074e8a75..e295b5eb25 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3804,6 +3804,7 @@ void ApiWrap::editMedia( .album = nullptr, .forceFile = forceFile, .idOverride = 0, + .displayName = file.displayName, })); } @@ -3867,6 +3868,7 @@ void ApiWrap::sendFiles( .album = album, .forceFile = forceFile, .idOverride = 0, + .displayName = file.displayName, })); caption = TextWithTags(); } diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 7718db922e..b8d7e9a34c 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -62,12 +62,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" #include "styles/style_layers.h" +#include "styles/style_settings.h" +#include "styles/style_menu_icons.h" #include namespace { constexpr auto kMaxMessageLength = 4096; +constexpr auto kMaxDisplayNameLength = 64; using Ui::SendFilesWay; @@ -113,6 +116,55 @@ rpl::producer FieldPlaceholder( : tr::lng_photos_comment(); } +void RenameFileBox( + not_null box, + const QString ¤tName, + Fn apply) { + box->setTitle(tr::lng_rename_file()); + const auto field = box->addRow(object_ptr( + box, + st::settingsDeviceName, + rpl::single(QString()), + currentName)); + const auto extension = [&] { + const auto dot = currentName.lastIndexOf('.'); + return (dot >= 0) ? currentName.mid(dot) : QString(); + }(); + const auto nameWithoutExt = extension.isEmpty() + ? currentName + : currentName.left(currentName.size() - extension.size()); + const auto maxNameLength = kMaxDisplayNameLength - extension.size(); + field->setMaxLength((maxNameLength > 0) ? maxNameLength : 0); + field->setText(nameWithoutExt); + field->selectAll(); + box->setFocusCallback([=] { + field->setFocusFast(); + }); + const auto save = [=] { + const auto newName = field->getLastText().trimmed(); + if (newName.isEmpty()) { + field->showError(); + return; + } + if ((newName.size() + extension.size()) > kMaxDisplayNameLength) { + field->showError(); + return; + } + const auto weak = base::make_weak(box); + apply(newName + extension); + if (const auto strong = weak.get()) { + strong->closeBox(); + } + }; + field->submits() | rpl::on_next([=] { + save(); + }, box->lifetime()); + box->addButton(tr::lng_settings_save(), save); + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); +} + void EditPriceBox( not_null box, not_null session, @@ -469,6 +521,16 @@ QImage SendFilesBox::Block::generatePriceTagBackground() const { return QImage(); } +bool SendFilesBox::Block::setSingleFileDisplayName( + const QString &displayName) { + if (_isAlbum || _isSingleMedia) { + return false; + } + const auto single = static_cast(_preview.get()); + single->setDisplayName(displayName); + return true; +} + SendFilesBox::SendFilesBox( QWidget*, not_null controller, @@ -689,6 +751,18 @@ void SendFilesBox::refreshAllAfterChanges(int fromItem, Fn perform) { captionResized(); } +bool SendFilesBox::setDisplayNameInSingleFilePreview( + int fileIndex, + const QString &displayName) { + for (auto &block : _blocks) { + if (fileIndex < block.fromIndex() || fileIndex >= block.tillIndex()) { + continue; + } + return block.setSingleFileDisplayName(displayName); + } + return false; +} + void SendFilesBox::openDialogToAddFileToAlbum() { const auto show = uiShow(); const auto checkResult = [=](const Ui::PreparedList &list) { @@ -1283,6 +1357,40 @@ void SendFilesBox::pushBlock(int from, int till) { _priceTag->update(); } }, widget->lifetime()); + + struct State { + base::unique_qptr menu; + }; + const auto state = widget->lifetime().make_state(); + base::install_event_filter(widget, [=, from = from, till = till]( + not_null e) { + if (e->type() == QEvent::ContextMenu) { + const auto mouse = static_cast(e.get()); + if (from >= till || from >= _list.files.size()) { + return base::EventFilterResult::Continue; + } + const auto fileIndex = from; + state->menu = base::make_unique_q( + widget, + _st.tabbed.menu); + state->menu->addAction(tr::lng_rename_file(tr::now), [=] { + auto &file = _list.files[fileIndex]; + _show->show(Box(RenameFileBox, file.displayName, [=]( + QString newName) { + const auto displayName = std::move(newName); + _list.files[fileIndex].displayName = displayName; + if (!setDisplayNameInSingleFilePreview( + fileIndex, + displayName)) { + refreshAllAfterChanges(from); + } + })); + }, &st::menuIconEdit); + state->menu->popup(mouse->globalPos()); + return base::EventFilterResult::Cancel; + } + return base::EventFilterResult::Continue; + }, widget->lifetime()); } void SendFilesBox::refreshControls(bool initial) { diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index 4d2db047e5..0e47358424 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -177,6 +177,8 @@ private: void applyChanges(); [[nodiscard]] QImage generatePriceTagBackground() const; + [[nodiscard]] bool setSingleFileDisplayName( + const QString &displayName); private: base::unique_qptr _preview; @@ -243,6 +245,9 @@ private: void openDialogToAddFileToAlbum(); void refreshAllAfterChanges(int fromItem, Fn perform = nullptr); + [[nodiscard]] bool setDisplayNameInSingleFilePreview( + int fileIndex, + const QString &displayName); void enqueueNextPrepare(); void addPreparedAsyncFile(Ui::PreparedFile &&file); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 34be9f435d..5ecabb50a9 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -473,6 +473,7 @@ FileLoadTask::FileLoadTask(Args &&args) , _to(std::move(args.to)) , _album(std::move(args.album)) , _filepath(std::move(args.filepath)) +, _displayName(std::move(args.displayName)) , _content(std::move(args.content)) , _videoCover(std::move(args.videoCover)) , _information(std::move(args.information)) @@ -810,7 +811,11 @@ void FileLoadTask::process(ProcessArgs &&args) { QImage goodThumbnail; QByteArray goodThumbnailBytes; - QVector attributes(1, MTP_documentAttributeFilename(MTP_string(filename))); + auto attributes = QVector( + 1, + MTP_documentAttributeFilename(MTP_string(_displayName.isEmpty() + ? filename + : _displayName))); auto thumbnail = PreparedFileThumbnail(); diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 2c011f45fd..e2c3832b8b 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -233,6 +233,7 @@ public: std::shared_ptr album; bool forceFile = false; uint64 idOverride = 0; + QString displayName; }; struct VoiceArgs { @@ -292,6 +293,7 @@ private: FileLoadTo _to; const std::shared_ptr _album; QString _filepath; + QString _displayName; QByteArray _content; std::unique_ptr _videoCover; std::unique_ptr _information; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp index 77e8bfd538..5ad8e15d18 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.cpp @@ -68,6 +68,13 @@ rpl::producer<> AbstractSingleFilePreview::clearCoverRequests() const { return rpl::never<>(); } +void AbstractSingleFilePreview::setDisplayName(const QString &displayName) { + auto data = _data; + data.name = displayName; + setData(data); + update(); +} + void AbstractSingleFilePreview::prepareThumbFor( Data &data, const QImage &preview) { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h index eef735f265..5fd830725a 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_abstract_single_file_preview.h @@ -32,6 +32,7 @@ public: [[nodiscard]] rpl::producer<> modifyRequests() const override; [[nodiscard]] rpl::producer<> editCoverRequests() const override; [[nodiscard]] rpl::producer<> clearCoverRequests() const override; + virtual void setDisplayName(const QString &displayName); protected: struct Data { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index 3cdae65132..73a13eeb80 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "core/mime_type.h" +#include + namespace Ui { namespace { @@ -23,6 +25,8 @@ constexpr auto kMaxAlbumCount = 10; } // namespace PreparedFile::PreparedFile(const QString &path) : path(path) { + const auto fileInfo = QFileInfo(path); + displayName = fileInfo.fileName(); } PreparedFile::PreparedFile(PreparedFile &&other) = default; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index 311903a00f..635be179ad 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -79,6 +79,7 @@ struct PreparedFile { [[nodiscard]] bool isGifv() const; QString path; + QString displayName; QByteArray content; int64 size = 0; std::unique_ptr information; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp index 344b30dd2c..c7ad3d0bef 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp @@ -27,6 +27,10 @@ SingleFilePreview::SingleFilePreview( preparePreview(file); } +void SingleFilePreview::setDisplayName(const QString &displayName) { + AbstractSingleFilePreview::setDisplayName(displayName); +} + void SingleFilePreview::preparePreview(const PreparedFile &file) { AbstractSingleFilePreview::Data data; @@ -41,13 +45,18 @@ void SingleFilePreview::preparePreview(const PreparedFile &file) { prepareThumbFor(data, preview); const auto filepath = file.path; if (filepath.isEmpty()) { - auto filename = "image.png"; - data.name = filename; + const auto fallbackName = u"image.png"_q; + const auto displayName = file.displayName.isEmpty() + ? fallbackName + : file.displayName; + data.name = displayName; data.statusText = FormatImageSizeText(file.originalDimensions); data.fileIsImage = true; } else { auto fileinfo = QFileInfo(filepath); - auto filename = fileinfo.fileName(); + auto filename = file.displayName.isEmpty() + ? fileinfo.fileName() + : file.displayName; data.fileIsImage = Core::FileIsImage( filename, Core::MimeTypeForFile(fileinfo).name()); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h index 24647c9174..4fc1f3b53a 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h @@ -20,6 +20,7 @@ public: const style::ComposeControls &st, const PreparedFile &file, AttachControls::Type type = AttachControls::Type::Full); + void setDisplayName(const QString &displayName) override; private: void preparePreview(const PreparedFile &file);