From 7425e80f05b5f8dc966eb784ca76828f6afdb435 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 14 Jan 2018 19:02:25 +0300 Subject: [PATCH] Use HistoryMedia as view, add Data::Media. --- Telegram/SourceFiles/apiwrap.cpp | 23 +- Telegram/SourceFiles/app.cpp | 44 +- Telegram/SourceFiles/app.h | 1 - Telegram/SourceFiles/auth_session.h | 2 + .../SourceFiles/base/runtime_composer.cpp | 2 +- Telegram/SourceFiles/base/runtime_composer.h | 75 +- .../SourceFiles/boxes/edit_caption_box.cpp | 57 +- Telegram/SourceFiles/boxes/edit_caption_box.h | 6 +- .../calls/calls_box_controller.cpp | 11 +- Telegram/SourceFiles/data/data_document.cpp | 23 +- Telegram/SourceFiles/data/data_groups.cpp | 49 + Telegram/SourceFiles/data/data_groups.h | 32 + .../SourceFiles/data/data_media_types.cpp | 932 ++++++++++++++ Telegram/SourceFiles/data/data_media_types.h | 347 ++++++ Telegram/SourceFiles/data/data_photo.cpp | 2 +- Telegram/SourceFiles/data/data_session.cpp | 93 +- Telegram/SourceFiles/data/data_session.h | 59 +- .../SourceFiles/data/data_shared_media.cpp | 7 +- Telegram/SourceFiles/data/data_types.h | 28 + Telegram/SourceFiles/facades.cpp | 7 +- .../admin_log/history_admin_log_inner.cpp | 36 +- .../admin_log/history_admin_log_item.cpp | 21 +- Telegram/SourceFiles/history/history.cpp | 853 +++++++------ Telegram/SourceFiles/history/history.h | 39 +- .../history/history_inner_widget.cpp | 174 +-- .../history/history_inner_widget.h | 2 +- Telegram/SourceFiles/history/history_item.cpp | 487 ++++---- Telegram/SourceFiles/history/history_item.h | 118 +- .../history/history_item_components.cpp | 26 +- .../history/history_item_components.h | 70 +- .../SourceFiles/history/history_media.cpp | 9 +- Telegram/SourceFiles/history/history_media.h | 44 +- .../history/history_media_grouped.cpp | 205 ++-- .../history/history_media_grouped.h | 30 +- .../history/history_media_types.cpp | 1068 +++++------------ .../SourceFiles/history/history_media_types.h | 250 +--- .../SourceFiles/history/history_message.cpp | 742 +++--------- .../SourceFiles/history/history_message.h | 42 +- .../SourceFiles/history/history_service.cpp | 101 +- .../SourceFiles/history/history_service.h | 15 +- .../SourceFiles/history/history_widget.cpp | 71 +- Telegram/SourceFiles/history/history_widget.h | 4 +- .../view/history_view_cursor_state.cpp | 19 + .../history/view/history_view_cursor_state.h | 13 +- .../history/view/history_view_element.cpp | 242 +++- .../history/view/history_view_element.h | 80 +- .../history/view/history_view_list_widget.cpp | 36 +- .../history/view/history_view_message.cpp | 700 +++++++++-- .../history/view/history_view_message.h | 69 +- .../view/history_view_service_message.cpp | 24 +- .../view/history_view_service_message.h | 4 + .../info/media/info_media_list_widget.cpp | 20 +- Telegram/SourceFiles/layout.h | 4 +- Telegram/SourceFiles/mainwidget.cpp | 9 +- .../media/player/media_player_float.cpp | 34 +- .../media/player/media_player_instance.cpp | 21 +- .../media/player/media_player_panel.cpp | 6 +- .../media/view/media_view_group_thumbs.cpp | 7 +- Telegram/SourceFiles/mediaview.cpp | 17 +- Telegram/SourceFiles/mtproto/type_utils.h | 4 +- .../SourceFiles/overview/overview_layout.cpp | 11 +- .../SourceFiles/overview/overview_layout.h | 8 +- Telegram/SourceFiles/ui/text/text.cpp | 29 +- Telegram/SourceFiles/ui/text/text.h | 4 +- .../window/notifications_manager.cpp | 21 +- Telegram/gyp/telegram_sources.txt | 4 + 66 files changed, 4463 insertions(+), 3130 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_groups.cpp create mode 100644 Telegram/SourceFiles/data/data_groups.h create mode 100644 Telegram/SourceFiles/data/data_media_types.cpp create mode 100644 Telegram/SourceFiles/data/data_media_types.h diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 195d1a9055..e2ebfc26fa 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_web_page.h" #include "data/data_feed.h" +#include "data/data_media_types.h" #include "core/tl_help.h" #include "base/overload.h" #include "observer_peer.h" @@ -2936,13 +2937,13 @@ void ApiWrap::sendUploadedPhoto( const MTPInputFile &file, bool silent) { if (const auto item = App::histItemById(localId)) { - const auto caption = item->getMedia() - ? item->getMedia()->getCaption() - : TextWithEntities(); + const auto caption = item->media() + ? item->media()->caption() + : QString(); const auto media = MTP_inputMediaUploadedPhoto( MTP_flags(0), file, - MTP_string(caption.text), + MTP_string(caption), MTPVector(), MTP_int(0)); if (const auto groupId = item->groupId()) { @@ -2959,9 +2960,9 @@ void ApiWrap::sendUploadedDocument( const base::optional &thumb, bool silent) { if (const auto item = App::histItemById(localId)) { - auto media = item->getMedia(); - if (auto document = media ? media->getDocument() : nullptr) { - const auto caption = media->getCaption(); + auto media = item->media(); + if (auto document = media ? media->document() : nullptr) { + const auto caption = media->caption(); const auto groupId = item->groupId(); const auto flags = MTPDinputMediaUploadedDocument::Flags(0) | (thumb @@ -2976,7 +2977,7 @@ void ApiWrap::sendUploadedDocument( thumb ? *thumb : MTPInputFile(), MTP_string(document->mimeString()), ComposeSendingDocumentAttributes(document), - MTP_string(caption.text), + MTP_string(caption), MTPVector(), MTP_int(0)); if (groupId) { @@ -3012,10 +3013,10 @@ void ApiWrap::uploadAlbumMedia( if (!item) { failed(); } - if (const auto media = item->getMedia()) { - if (const auto photo = media->getPhoto()) { + if (const auto media = item->media()) { + if (const auto photo = media->photo()) { photo->setWaitingForAlbum(); - } else if (const auto document = media->getDocument()) { + } else if (const auto document = media->document()) { document->setWaitingForAlbum(); } } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 9de0c95711..e7899c828a 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "lang/lang_keys.h" #include "data/data_abstract_structure.h" +#include "data/data_media_types.h" #include "data/data_session.h" #include "history/history.h" #include "history/history_location_manager.h" @@ -978,10 +979,10 @@ namespace { void checkSavedGif(HistoryItem *item) { if (!item->Has() && (item->out() || item->history()->peer == App::self())) { - if (auto media = item->getMedia()) { - if (auto doc = media->getDocument()) { - if (doc->isGifv()) { - addSavedGif(doc); + if (const auto media = item->media()) { + if (const auto document = media->document()) { + if (document->isGifv()) { + addSavedGif(document); } } } @@ -1836,24 +1837,6 @@ namespace { } } - void messageViewDestroyed(not_null view) { - if (::hoveredItem == view) { - hoveredItem(nullptr); - } - if (::pressedItem == view) { - pressedItem(nullptr); - } - if (::hoveredLinkItem == view) { - hoveredLinkItem(nullptr); - } - if (::pressedLinkItem == view) { - pressedLinkItem(nullptr); - } - if (::mousedItem == view) { - mousedItem(nullptr); - } - } - void historyUnregItem(HistoryItem *item) { auto data = fetchMsgsData(item->channelId(), false); if (!data) return; @@ -2474,11 +2457,12 @@ namespace { if (!::gifItems.isEmpty()) { auto gifs = ::gifItems; for_const (auto item, gifs) { - if (auto media = item->getMedia()) { - if (!media->isRoundVideoPlaying()) { - media->stopInline(); - } - } + // #TODO GIFs + //if (auto media = item->getMedia()) { + // if (!media->isRoundVideoPlaying()) { + // media->stopInline(); + // } + //} } } } @@ -2486,9 +2470,9 @@ namespace { QString phoneFromSharedContact(int32 userId) { auto i = ::sharedContactItems.constFind(userId); if (i != ::sharedContactItems.cend() && !i->empty()) { - if (auto media = (*i->cbegin())->getMedia()) { - if (media->type() == MediaTypeContact) { - return static_cast(media)->phone(); + if (const auto media = (*i->cbegin())->media()) { + if (const auto contact = media->sharedContact()) { + return contact->phoneNumber; } } } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index fe1d48fa17..79069beeba 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -237,7 +237,6 @@ namespace App { void historyClearItems(); void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency); void historyUnregDependency(HistoryItem *dependent, HistoryItem *dependency); - void messageViewDestroyed(not_null view); void historyRegRandom(uint64 randomId, const FullMsgId &itemId); void historyUnregRandom(uint64 randomId); diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h index 6c14b451b7..fd9b87175c 100644 --- a/Telegram/SourceFiles/auth_session.h +++ b/Telegram/SourceFiles/auth_session.h @@ -259,4 +259,6 @@ private: const std::unique_ptr _notifications; const std::unique_ptr _changelogs; + rpl::lifetime _lifetime; + }; diff --git a/Telegram/SourceFiles/base/runtime_composer.cpp b/Telegram/SourceFiles/base/runtime_composer.cpp index b5ec992916..57051c207b 100644 --- a/Telegram/SourceFiles/base/runtime_composer.cpp +++ b/Telegram/SourceFiles/base/runtime_composer.cpp @@ -31,7 +31,7 @@ const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask) { return i.value(); } -const RuntimeComposerMetadata *RuntimeComposer::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0); +const RuntimeComposerMetadata *RuntimeComposerBase::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0); RuntimeComponentWrapStruct RuntimeComponentWraps[64]; diff --git a/Telegram/SourceFiles/base/runtime_composer.h b/Telegram/SourceFiles/base/runtime_composer.h index 6602c6e10e..e04c8e2a40 100644 --- a/Telegram/SourceFiles/base/runtime_composer.h +++ b/Telegram/SourceFiles/base/runtime_composer.h @@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +template class RuntimeComposer; -typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposer *composer); + +class RuntimeComposerBase; +typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposerBase *composer); typedef void(*RuntimeComponentDestruct)(void *location); typedef void(*RuntimeComponentMove)(void *location, void *waslocation); @@ -38,8 +41,10 @@ struct CeilDivideMinimumOne { extern RuntimeComponentWrapStruct RuntimeComponentWraps[64]; extern QAtomicInt RuntimeComponentIndexLast; -template +template struct RuntimeComponent { + using RuntimeComponentBase = Base; + RuntimeComponent() { // While there is no std::aligned_alloc(). static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!"); @@ -76,7 +81,7 @@ struct RuntimeComponent { } protected: - static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) { + static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) { new (location) Type(); } static void RuntimeComponentDestruct(void *location) { @@ -134,9 +139,9 @@ private: const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask); -class RuntimeComposer { +class RuntimeComposerBase { public: - RuntimeComposer(uint64 mask = 0) : _data(zerodata()) { + RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) { if (mask) { auto meta = GetRuntimeComposerMetadata(mask); @@ -169,9 +174,9 @@ public: } } } - RuntimeComposer(const RuntimeComposer &other) = delete; - RuntimeComposer &operator=(const RuntimeComposer &other) = delete; - ~RuntimeComposer() { + RuntimeComposerBase(const RuntimeComposerBase &other) = delete; + RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete; + ~RuntimeComposerBase() { if (_data != zerodata()) { auto meta = _meta(); for (int i = 0; i < meta->last; ++i) { @@ -184,24 +189,10 @@ public: } } - template - bool Has() const { - return (_meta()->offsets[Type::Index()] >= sizeof(_meta())); - } - - template - Type *Get() { - return static_cast(_dataptr(_meta()->offsets[Type::Index()])); - } - template - const Type *Get() const { - return static_cast(_dataptr(_meta()->offsets[Type::Index()])); - } - protected: void UpdateComponents(uint64 mask = 0) { if (!_meta()->equals(mask)) { - RuntimeComposer tmp(mask); + RuntimeComposerBase tmp(mask); tmp.swap(*this); if (_data != zerodata() && tmp._data != zerodata()) { auto meta = _meta(), wasmeta = tmp._meta(); @@ -223,6 +214,9 @@ protected: } private: + template + friend class RuntimeComposer; + static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata; static void *zerodata() { return &ZeroRuntimeComposerMetadata; @@ -239,8 +233,41 @@ private: } void *_data = nullptr; - void swap(RuntimeComposer &other) { + void swap(RuntimeComposerBase &other) { std::swap(_data, other._data); } }; + +template +class RuntimeComposer : public RuntimeComposerBase { +public: + using RuntimeComposerBase::RuntimeComposerBase; + + template < + typename Type, + typename = std::enable_if_t>> + bool Has() const { + return (_meta()->offsets[Type::Index()] >= sizeof(_meta())); + } + + template < + typename Type, + typename = std::enable_if_t>> + Type *Get() { + return static_cast(_dataptr(_meta()->offsets[Type::Index()])); + } + template < + typename Type, + typename = std::enable_if_t>> + const Type *Get() const { + return static_cast(_dataptr(_meta()->offsets[Type::Index()])); + } + +}; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 6fd79fa0a2..703267c453 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/media_clip_reader.h" #include "history/history.h" #include "history/history_item.h" -#include "history/history_media_types.h" +#include "data/data_media_types.h" +#include "data/data_photo.h" +#include "data/data_document.h" #include "lang/lang_keys.h" #include "window/window_controller.h" #include "mainwidget.h" @@ -22,59 +24,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL EditCaptionBox::EditCaptionBox( QWidget*, - not_null media, + not_null media, FullMsgId msgId) : _msgId(msgId) { - Expects(media->canEditCaption()); + Expects(media->allowsEditCaption()); QSize dimensions; ImagePtr image; - QString caption; DocumentData *doc = nullptr; - switch (media->type()) { - case MediaTypeGif: { - _animated = true; - doc = static_cast(media.get())->getDocument(); - dimensions = doc->dimensions; - image = doc->thumb; - } break; - - case MediaTypePhoto: { + if (const auto photo = media->photo()) { _photo = true; - auto photo = static_cast(media.get())->getPhoto(); dimensions = QSize(photo->full->width(), photo->full->height()); image = photo->full; - } break; - - case MediaTypeVideo: { - _animated = true; - doc = static_cast(media.get())->getDocument(); - dimensions = doc->dimensions; - image = doc->thumb; - } break; - - case MediaTypeGrouped: { - if (const auto photo = media->getPhoto()) { - dimensions = QSize(photo->full->width(), photo->full->height()); - image = photo->full; - _photo = true; - } else if (const auto doc = media->getDocument()) { - dimensions = doc->dimensions; - image = doc->thumb; + } else if (const auto document = media->document()) { + dimensions = document->dimensions; + image = document->thumb; + if (document->isAnimation()) { _animated = true; + } else if (document->isVideoFile()) { + _animated = true; + } else { + _doc = true; } - } break; - - case MediaTypeFile: - case MediaTypeMusicFile: - case MediaTypeVoiceFile: { - _doc = true; - doc = static_cast(media.get())->getDocument(); - image = doc->thumb; - } break; + doc = document; } - caption = media->getCaption().text; + auto caption = media->caption(); if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) { if (image->isNull()) { diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index 2b2a63cfb0..eff1d52176 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" -class HistoryMedia; +namespace Data { +class Media; +} // namespace Data namespace Ui { class InputArea; @@ -17,7 +19,7 @@ class InputArea; class EditCaptionBox : public BoxContent, public RPCSender { public: - EditCaptionBox(QWidget*, not_null media, FullMsgId msgId); + EditCaptionBox(QWidget*, not_null media, FullMsgId msgId); protected: void prepare() override; diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 313067412d..f1e57e53bd 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -15,10 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_instance.h" #include "history/history.h" #include "history/history_item.h" -#include "history/history_media_types.h" #include "mainwidget.h" #include "auth_session.h" #include "data/data_session.h" +#include "data/data_media_types.h" namespace Calls { namespace { @@ -178,10 +178,11 @@ BoxController::Row::Type BoxController::Row::ComputeType( not_null item) { if (item->out()) { return Type::Out; - } else if (auto media = item->getMedia()) { - if (media->type() == MediaTypeCall) { - auto reason = static_cast(media)->reason(); - if (reason == HistoryCall::FinishReason::Busy || reason == HistoryCall::FinishReason::Missed) { + } else if (auto media = item->media()) { + if (const auto call = media->call()) { + const auto reason = call->finishReason; + if (reason == Data::Call::FinishReason::Busy + || reason == Data::Call::FinishReason::Missed) { return Type::Missed; } } diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 38f1507b75..037c0c4d3c 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -285,15 +285,15 @@ void DocumentOpenClickHandler::doOpen( if (App::main()) App::main()->mediaMarkRead(data); } else if (data->size < App::kImageSizeLimit) { if (!data->data().isEmpty() && playAnimation) { - if (action == ActionOnLoadPlayInline && context && context->getMedia()) { - context->getMedia()->playInline(); + if (action == ActionOnLoadPlayInline && context) { + Auth().data().requestItemPlayInline(context); } else { Messenger::Instance().showDocument(data, context); } } else if (location.accessEnable()) { if (data->isAnimation() || QImageReader(location.name()).canRead()) { - if (action == ActionOnLoadPlayInline && context && context->getMedia()) { - context->getMedia()->playInline(); + if (action == ActionOnLoadPlayInline && context) { + Auth().data().requestItemPlayInline(context); } else { Messenger::Instance().showDocument(data, context); } @@ -548,7 +548,10 @@ void DocumentData::performActionOnLoad() { auto showImage = !isVideoFile() && (size < App::kImageSizeLimit); auto playVoice = isVoiceMessage() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); auto playMusic = isAudioFile() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen); - auto playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia(); + auto playAnimation = isAnimation() + && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) + && showImage + && item; if (auto applyTheme = isTheme()) { if (!loc.isEmpty() && loc.accessEnable()) { Messenger::Instance().showDocument(this, item); @@ -588,8 +591,8 @@ void DocumentData::performActionOnLoad() { } } else if (playAnimation) { if (loaded()) { - if (_actionOnLoad == ActionOnLoadPlayInline && item->getMedia()) { - item->getMedia()->playInline(); + if (_actionOnLoad == ActionOnLoadPlayInline && item) { + Auth().data().requestItemPlayInline(item); } else { Messenger::Instance().showDocument(this, item); } @@ -607,8 +610,8 @@ void DocumentData::performActionOnLoad() { if (App::main()) App::main()->mediaMarkRead(this); } else if (loc.accessEnable()) { if (showImage && QImageReader(loc.name()).canRead()) { - if (_actionOnLoad == ActionOnLoadPlayInline && item && item->getMedia()) { - item->getMedia()->playInline(); + if (_actionOnLoad == ActionOnLoadPlayInline && item) { + Auth().data().requestItemPlayInline(item); } else { Messenger::Instance().showDocument(this, item); } @@ -764,7 +767,7 @@ void DocumentData::cancel() { void DocumentData::notifyLayoutChanged() const { auto &items = App::documentItems(); for (auto item : items.value(const_cast(this))) { - Auth().data().markItemLayoutChanged(item); + Auth().data().markItemLayoutChange(item); } if (auto items = InlineBots::Layout::documentItems()) { diff --git a/Telegram/SourceFiles/data/data_groups.cpp b/Telegram/SourceFiles/data/data_groups.cpp new file mode 100644 index 0000000000..bb1d883dac --- /dev/null +++ b/Telegram/SourceFiles/data/data_groups.cpp @@ -0,0 +1,49 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "data/data_groups.h" + +#include "history/history_item.h" + +namespace Data { + +void Groups::registerMessage(not_null item) { + const auto groupId = item->groupId(); + if (!groupId) { + return; + } + const auto i = _data.emplace(groupId, Group()).first; + i->second.items.push_back(item); +} + +void Groups::unregisterMessage(not_null item) { + const auto groupId = item->groupId(); + if (!groupId) { + return; + } + const auto i = _data.find(groupId); + if (i != _data.end()) { + auto &group = i->second; + group.items.erase( + ranges::remove(group.items, item), + group.items.end()); + if (group.items.empty()) { + _data.erase(i); + } + } +} + +const Group *Groups::find(not_null item) const { + const auto groupId = item->groupId(); + if (!groupId) { + return nullptr; + } + const auto i = _data.find(groupId); + return (i != _data.end()) ? &i->second : nullptr; +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_groups.h b/Telegram/SourceFiles/data/data_groups.h new file mode 100644 index 0000000000..fc4a232e8c --- /dev/null +++ b/Telegram/SourceFiles/data/data_groups.h @@ -0,0 +1,32 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "data/data_types.h" + +namespace Data { + +struct Group { + HistoryItemsList items; + +}; + +class Groups { +public: + void registerMessage(not_null item); + void unregisterMessage(not_null item); + + const Group *find(not_null item) const; + +private: + std::map _data; + std::map _alias; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp new file mode 100644 index 0000000000..ee4f11788a --- /dev/null +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -0,0 +1,932 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "data/data_media_types.h" + +#include "history/history_media_types.h" +#include "history/history_item.h" +#include "history/history_location_manager.h" +#include "storage/storage_shared_media.h" +#include "storage/localstorage.h" +#include "lang/lang_keys.h" +#include "layout.h" + +namespace Data { +namespace { + +Call ComputeCallData(const MTPDmessageActionPhoneCall &call) { + auto result = Call(); + result.finishReason = [&] { + if (call.has_reason()) { + switch (call.vreason.type()) { + case mtpc_phoneCallDiscardReasonBusy: + return CallFinishReason::Busy; + case mtpc_phoneCallDiscardReasonDisconnect: + return CallFinishReason::Disconnected; + case mtpc_phoneCallDiscardReasonHangup: + return CallFinishReason::Hangup; + case mtpc_phoneCallDiscardReasonMissed: + return CallFinishReason::Missed; + } + Unexpected("Call reason type."); + } + return CallFinishReason::Hangup; + }(); + result.duration = call.has_duration() ? call.vduration.v : 0;; + return result; +} + +Invoice ComputeInvoiceData(const MTPDmessageMediaInvoice &data) { + auto result = Invoice(); + result.isTest = data.is_test(); + result.amount = data.vtotal_amount.v; + result.currency = qs(data.vcurrency); + result.description = qs(data.vdescription); + result.title = TextUtilities::SingleLine(qs(data.vtitle)); + if (data.has_receipt_msg_id()) { + result.receiptMsgId = data.vreceipt_msg_id.v; + } + if (data.has_photo() && data.vphoto.type() == mtpc_webDocument) { + auto &doc = data.vphoto.c_webDocument(); + auto imageSize = QSize(); + for (auto &attribute : doc.vattributes.v) { + if (attribute.type() == mtpc_documentAttributeImageSize) { + auto &size = attribute.c_documentAttributeImageSize(); + imageSize = QSize(size.vw.v, size.vh.v); + break; + } + } + if (!imageSize.isEmpty()) { + auto thumbsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 100, 100); + auto thumb = ImagePtr(thumbsize.width(), thumbsize.height()); + + auto mediumsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 320, 320); + auto medium = ImagePtr(mediumsize.width(), mediumsize.height()); + + // We don't use size from WebDocument, because it is not reliable. + // It can be > 0 and different from the real size that we get in upload.WebFile result. + auto filesize = 0; // doc.vsize.v; + auto full = ImagePtr(WebFileImageLocation(imageSize.width(), imageSize.height(), doc.vdc_id.v, doc.vurl.v, doc.vaccess_hash.v), filesize); + auto photoId = rand_value(); + result.photo = App::photoSet(photoId, 0, 0, unixtime(), thumb, medium, full); + } + } + return result; +} + +QString WithCaptionDialogsText( + const QString &attachType, + const QString &caption) { + if (caption.isEmpty()) { + return textcmdLink(1, TextUtilities::Clean(attachType)); + } + + auto captionText = TextUtilities::Clean(caption); + auto attachTypeWrapped = textcmdLink(1, lng_dialogs_text_media_wrapped( + lt_media, + TextUtilities::Clean(attachType))); + return lng_dialogs_text_media( + lt_media_part, + attachTypeWrapped, + lt_caption, + captionText); +} + +QString WithCaptionNotificationText( + const QString &attachType, + const QString &caption) { + if (caption.isEmpty()) { + return attachType; + } + + auto attachTypeWrapped = lng_dialogs_text_media_wrapped( + lt_media, + attachType); + return lng_dialogs_text_media( + lt_media_part, + attachTypeWrapped, + lt_caption, + caption); +} + +} // namespace + +Media::Media(not_null parent) : _parent(parent) { +} + +not_null Media::parent() const { + return _parent; +} + +DocumentData *Media::document() const { + return nullptr; +} + +PhotoData *Media::photo() const { + return nullptr; +} + +WebPageData *Media::webpage() const { + return nullptr; +} + +const SharedContact *Media::sharedContact() const { + return nullptr; +} + +const Call *Media::call() const { + return nullptr; +} + +GameData *Media::game() const { + return nullptr; +} + +const Invoice *Media::invoice() const { + return nullptr; +} + +LocationData *Media::location() const { + return nullptr; +} + +bool Media::uploading() const { + return false; +} + +Storage::SharedMediaTypesMask Media::sharedMediaTypes() const { + return {}; +} + +QString Media::caption() const { + return QString(); +} + +QString Media::chatsListText() const { + auto result = notificationText(); + return result.isEmpty() + ? QString() + : textcmdLink(1, TextUtilities::Clean(std::move(result))); +} + +bool Media::hasReplyPreview() const { + return false; +} + +ImagePtr Media::replyPreview() const { + return ImagePtr(); +} + +bool Media::allowsForward() const { + return true; +} + +bool Media::allowsEdit() const { + return allowsEditCaption(); +} + +bool Media::allowsEditCaption() const { + return false; +} + +bool Media::allowsRevoke() const { + return true; +} + +bool Media::forwardedBecomesUnread() const { + return false; +} + +QString Media::errorTextForForward(not_null channel) const { + return QString(); +} + +bool Media::consumeMessageText(const TextWithEntities &text) { + return false; +} + +MediaPhoto::MediaPhoto( + not_null parent, + not_null photo, + const QString &caption) +: Media(parent) +, _photo(photo) +, _caption(caption) { + App::regPhotoItem(_photo, parent); +} + +MediaPhoto::MediaPhoto( + not_null parent, + not_null chat, + not_null photo) +: Media(parent) +, _photo(photo) +, _chat(chat) { + App::regPhotoItem(_photo, parent); +} + +MediaPhoto::~MediaPhoto() { + App::unregPhotoItem(_photo, parent()); +} + +std::unique_ptr MediaPhoto::clone(not_null parent) { + return _chat + ? std::make_unique(parent, _chat, _photo) + : std::make_unique(parent, _photo, _caption); +} + +PhotoData *MediaPhoto::photo() const { + return _photo; +} + +bool MediaPhoto::uploading() const { + return _photo->uploading(); +} + +Storage::SharedMediaTypesMask MediaPhoto::sharedMediaTypes() const { + using Type = Storage::SharedMediaType; + if (_chat) { + return Type::ChatPhoto; + } + return Storage::SharedMediaTypesMask{} + .added(Type::Photo) + .added(Type::PhotoVideo); +} + +QString MediaPhoto::caption() const { + return _caption; +} + +QString MediaPhoto::notificationText() const { + return WithCaptionNotificationText(lang(lng_in_dlg_photo), _caption); + //return WithCaptionNotificationText(lang(lng_in_dlg_album), _caption); +} + +QString MediaPhoto::chatsListText() const { + return WithCaptionDialogsText(lang(lng_in_dlg_photo), _caption); + //return WithCaptionDialogsText(lang(lng_in_dlg_album), _caption); +} + +QString MediaPhoto::pinnedTextSubstring() const { + return lang(lng_action_pinned_media_photo); +} + +bool MediaPhoto::allowsEditCaption() const { + return true; +} + +QString MediaPhoto::errorTextForForward( + not_null channel) const { + if (channel->restricted(ChannelRestriction::f_send_media)) { + return lang(lng_restricted_send_media); + } + return QString(); +} + +bool MediaPhoto::updateInlineResultMedia(const MTPMessageMedia &media) { + if (media.type() != mtpc_messageMediaPhoto) { + return false; + } + auto &photo = media.c_messageMediaPhoto(); + if (photo.has_photo() && !photo.has_ttl_seconds()) { + if (auto existing = App::feedPhoto(photo.vphoto)) { + if (existing == _photo) { + return true; + } else { + // collect data + } + } + } else { + LOG(("API Error: " + "Got MTPMessageMediaPhoto without photo " + "or with ttl_seconds in updateInlineResultMedia()")); + } + // Can return false if we collect the data. + return true; +} + +bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) { + if (media.type() != mtpc_messageMediaPhoto) { + return false; + } + auto &mediaPhoto = media.c_messageMediaPhoto(); + if (!mediaPhoto.has_photo() || mediaPhoto.has_ttl_seconds()) { + LOG(("Api Error: " + "Got MTPMessageMediaPhoto without photo " + "or with ttl_seconds in updateSentMedia()")); + return false; + } + const auto &photo = mediaPhoto.vphoto; + App::feedPhoto(photo, _photo); + + if (photo.type() != mtpc_photo) { + return false; + } + auto &sizes = photo.c_photo().vsizes.v; + auto max = 0; + const MTPDfileLocation *maxLocation = 0; + for (const auto &data : sizes) { + char size = 0; + const MTPFileLocation *loc = 0; + switch (data.type()) { + case mtpc_photoSize: { + const auto &s = data.c_photoSize().vtype.v; + loc = &data.c_photoSize().vlocation; + if (s.size()) size = s[0]; + } break; + + case mtpc_photoCachedSize: { + const auto &s = data.c_photoCachedSize().vtype.v; + loc = &data.c_photoCachedSize().vlocation; + if (s.size()) size = s[0]; + } break; + } + if (!loc || loc->type() != mtpc_fileLocation) { + continue; + } + if (size == 's') { + Local::writeImage(storageKey(loc->c_fileLocation()), _photo->thumb); + } else if (size == 'm') { + Local::writeImage(storageKey(loc->c_fileLocation()), _photo->medium); + } else if (size == 'x' && max < 1) { + max = 1; + maxLocation = &loc->c_fileLocation(); + } else if (size == 'y' && max < 2) { + max = 2; + maxLocation = &loc->c_fileLocation(); + //} else if (size == 'w' && max < 3) { + // max = 3; + // maxLocation = &loc->c_fileLocation(); + } + } + if (maxLocation) { + Local::writeImage(storageKey(*maxLocation), _photo->full); + } + return true; +} + +std::unique_ptr MediaPhoto::createView( + not_null message) { + if (_chat) { + return std::make_unique( + message, + _chat, + _photo, + st::msgServicePhotoWidth); + } + return std::make_unique(message, _photo, _caption); +} + +MediaFile::MediaFile( + not_null parent, + not_null document, + const QString &caption) +: Media(parent) +, _document(document) +, _caption(caption) +, _emoji(document->sticker() ? document->sticker()->alt : QString()) { + App::regDocumentItem(_document, parent); + + if (!_emoji.isEmpty()) { + if (const auto emoji = Ui::Emoji::Find(_emoji)) { + _emoji = emoji->text(); + } + } +} + +MediaFile::~MediaFile() { + App::unregDocumentItem(_document, parent()); +} + +std::unique_ptr MediaFile::clone(not_null parent) { + return std::make_unique(parent, _document, _caption); +} + +DocumentData *MediaFile::document() const { + return _document; +} + +bool MediaFile::uploading() const { + return _document->uploading(); +} + +Storage::SharedMediaTypesMask MediaFile::sharedMediaTypes() const { + using Type = Storage::SharedMediaType; + if (_document->sticker()) { + return {}; + } else if (_document->isVideoMessage()) { + return Storage::SharedMediaTypesMask{} + .added(Type::RoundFile) + .added(Type::RoundVoiceFile); + } else if (_document->isGifv()) { + return Type::GIF; + } else if (_document->isVideoFile()) { + return Storage::SharedMediaTypesMask{} + .added(Type::Video) + .added(Type::PhotoVideo); + } else if (_document->isVoiceMessage()) { + return Storage::SharedMediaTypesMask{} + .added(Type::VoiceFile) + .added(Type::RoundVoiceFile); + } else if (_document->isSharedMediaMusic()) { + return Type::MusicFile; + } + return Type::File; +} + +QString MediaFile::chatsListText() const { + if (const auto sticker = _document->sticker()) { + return Media::chatsListText(); + } + const auto type = [&] { + if (_document->isVideoMessage()) { + return lang(lng_in_dlg_video_message); + } else if (_document->isAnimation()) { + return qsl("GIF"); + } else if (_document->isVideoFile()) { + return lang(lng_in_dlg_video); + } else if (_document->isVoiceMessage()) { + return lang(lng_in_dlg_audio); + } else if (!_document->filename().isEmpty()) { + return _document->filename(); + } else if (_document->isAudioFile()) { + return lang(lng_in_dlg_audio_file); + } + return lang(lng_in_dlg_file); + }(); + return WithCaptionDialogsText(type, _caption); +} + +QString MediaFile::notificationText() const { + if (const auto sticker = _document->sticker()) { + return _emoji.isEmpty() + ? lang(lng_in_dlg_sticker) + : lng_in_dlg_sticker_emoji(lt_emoji, _emoji); + } + const auto type = [&] { + if (_document->isVideoMessage()) { + return lang(lng_in_dlg_video_message); + } else if (_document->isAnimation()) { + return qsl("GIF"); + } else if (_document->isVideoFile()) { + return lang(lng_in_dlg_video); + } else if (_document->isVoiceMessage()) { + return lang(lng_in_dlg_audio); + } else if (!_document->filename().isEmpty()) { + return _document->filename(); + } else if (_document->isAudioFile()) { + return lang(lng_in_dlg_audio_file); + } + return lang(lng_in_dlg_file); + }(); + return WithCaptionNotificationText(type, _caption); +} + +QString MediaFile::pinnedTextSubstring() const { + if (const auto sticker = _document->sticker()) { + if (!_emoji.isEmpty()) { + return lng_action_pinned_media_emoji_sticker(lt_emoji, _emoji); + } + return lang(lng_action_pinned_media_sticker); + } else if (_document->isAnimation()) { + if (_document->isVideoMessage()) { + return lang(lng_action_pinned_media_video_message); + } + return lang(lng_action_pinned_media_gif); + } else if (_document->isVideoFile()) { + return lang(lng_action_pinned_media_video); + } else if (_document->isVoiceMessage()) { + return lang(lng_action_pinned_media_voice); + } else if (_document->isSong()) { + return lang(lng_action_pinned_media_audio); + } + return lang(lng_action_pinned_media_file); +} + +bool MediaFile::allowsEditCaption() const { + return !_document->isVideoMessage() && !_document->sticker(); +} + +bool MediaFile::forwardedBecomesUnread() const { + return _document->isVoiceMessage() + //|| _document->isVideoFile() + || _document->isVideoMessage(); +} + +QString MediaFile::errorTextForForward( + not_null channel) const { + if (const auto sticker = _document->sticker()) { + if (channel->restricted(ChannelRestriction::f_send_stickers)) { + return lang(lng_restricted_send_stickers); + } + } else if (_document->isAnimation()) { + if (_document->isVideoMessage()) { + if (channel->restricted(ChannelRestriction::f_send_media)) { + return lang(lng_restricted_send_media); + } + } else { + if (channel->restricted(ChannelRestriction::f_send_gifs)) { + return lang(lng_restricted_send_gifs); + } + } + } else if (channel->restricted(ChannelRestriction::f_send_media)) { + return lang(lng_restricted_send_media); + } + return QString(); +} + +bool MediaFile::updateInlineResultMedia(const MTPMessageMedia &media) { + if (media.type() != mtpc_messageMediaDocument) { + return false; + } + auto &data = media.c_messageMediaDocument(); + if (data.has_document() && !data.has_ttl_seconds()) { + if (const auto document = App::feedDocument(data.vdocument)) { + if (document == _document) { + return false; + } else { + document->collectLocalData(_document); + } + } + } else { + LOG(("API Error: " + "Got MTPMessageMediaDocument without document " + "or with ttl_seconds in updateInlineResultMedia()")); + } + return false; +} + +bool MediaFile::updateSentMedia(const MTPMessageMedia &media) { + if (media.type() != mtpc_messageMediaDocument) { + return false; + } + auto &data = media.c_messageMediaDocument(); + if (!data.has_document() || data.has_ttl_seconds()) { + LOG(("Api Error: " + "Got MTPMessageMediaDocument without document " + "or with ttl_seconds in updateSentMedia()")); + return false; + } + const auto changed = App::feedDocument(data.vdocument, _document); + if (!changed->data().isEmpty()) { + if (changed->isVoiceMessage()) { + Local::writeAudio(changed->mediaKey(), changed->data()); + } else { + Local::writeStickerImage(changed->mediaKey(), changed->data()); + } + } + return true; +} + +std::unique_ptr MediaFile::createView( + not_null message) { + if (_document->sticker()) { + return std::make_unique(message, _document); + } else if (_document->isAnimation()) { + return std::make_unique(message, _document, _caption); + } else if (_document->isVideoFile()) { + return std::make_unique(message, _document, _caption); + } + return std::make_unique(message, _document, _caption); +} + +MediaContact::MediaContact( + not_null parent, + UserId userId, + const QString &firstName, + const QString &lastName, + const QString &phoneNumber) +: Media(parent) { + _contact.userId = userId; + _contact.firstName = firstName; + _contact.lastName = lastName; + _contact.phoneNumber = phoneNumber; +} + +std::unique_ptr MediaContact::clone(not_null parent) { + return std::make_unique( + parent, + _contact.userId, + _contact.firstName, + _contact.lastName, + _contact.phoneNumber); +} + +const SharedContact *MediaContact::sharedContact() const { + return &_contact; +} + +QString MediaContact::notificationText() const { + return lang(lng_in_dlg_contact); +} + +QString MediaContact::pinnedTextSubstring() const { + return lang(lng_action_pinned_media_contact); +} + +bool MediaContact::updateInlineResultMedia(const MTPMessageMedia &media) { + return false; +} + +bool MediaContact::updateSentMedia(const MTPMessageMedia &media) { + if (media.type() != mtpc_messageMediaContact) { + return false; + } + if (_contact.userId != media.c_messageMediaContact().vuser_id.v) { + //detachFromParent(); // #TODO contacts + _contact.userId = media.c_messageMediaContact().vuser_id.v; + //attachToParent(); + } + return true; +} + +std::unique_ptr MediaContact::createView( + not_null message) { + return std::make_unique( + message, + _contact.userId, + _contact.firstName, + _contact.lastName, + _contact.phoneNumber); +} + +MediaLocation::MediaLocation( + not_null parent, + const LocationCoords &coords) +: MediaLocation(parent, coords, QString(), QString()) { +} + +MediaLocation::MediaLocation( + not_null parent, + const LocationCoords &coords, + const QString &title, + const QString &description) +: Media(parent) +, _location(App::location(coords)) +, _title(title) +, _description(description) { +} + +std::unique_ptr MediaLocation::clone(not_null parent) { + return std::make_unique( + parent, + _location->coords, + _title, + _description); +} + +LocationData *MediaLocation::location() const { + return _location; +} + +QString MediaLocation::chatsListText() const { + return WithCaptionDialogsText(lang(lng_maps_point), _title); +} + +QString MediaLocation::notificationText() const { + return WithCaptionNotificationText(lang(lng_maps_point), _title); +} + +QString MediaLocation::pinnedTextSubstring() const { + return lang(lng_action_pinned_media_location); +} + +bool MediaLocation::updateInlineResultMedia(const MTPMessageMedia &media) { + return false; +} + +bool MediaLocation::updateSentMedia(const MTPMessageMedia &media) { + return false; +} + +std::unique_ptr MediaLocation::createView( + not_null message) { + return std::make_unique( + message, + _location, + _title, + _description); +} + +MediaCall::MediaCall( + not_null parent, + const MTPDmessageActionPhoneCall &call) +: Media(parent) +, _call(ComputeCallData(call)) { +} + +std::unique_ptr MediaCall::clone(not_null parent) { + Unexpected("Clone of call media."); +} + +const Call *MediaCall::call() const { + return &_call; +} + +QString MediaCall::notificationText() const { + auto result = Text(parent(), _call.finishReason); + if (_call.duration > 0) { + result = lng_call_type_and_duration( + lt_type, + result, + lt_duration, + formatDurationWords(_call.duration)); + } + return result; +} + +QString MediaCall::pinnedTextSubstring() const { + return QString(); +} + +bool MediaCall::allowsForward() const { + return false; +} + +bool MediaCall::allowsRevoke() const { + return false; +} + +bool MediaCall::updateInlineResultMedia(const MTPMessageMedia &media) { + return false; +} + +bool MediaCall::updateSentMedia(const MTPMessageMedia &media) { + return false; +} + +std::unique_ptr MediaCall::createView( + not_null message) { + return std::make_unique(message, &_call); +} + +QString MediaCall::Text( + not_null item, + CallFinishReason reason) { + if (item->out()) { + return lang(reason == CallFinishReason::Missed + ? lng_call_cancelled + : lng_call_outgoing); + } else if (reason == CallFinishReason::Missed) { + return lang(lng_call_missed); + } else if (reason == CallFinishReason::Busy) { + return lang(lng_call_declined); + } + return lang(lng_call_incoming); +} + +MediaWebPage::MediaWebPage( + not_null parent, + not_null page) +: Media(parent) +, _page(page) { +} + +std::unique_ptr MediaWebPage::clone(not_null parent) { + return std::make_unique(parent, _page); +} + +WebPageData *MediaWebPage::webpage() const { + return _page; +} + +QString MediaWebPage::notificationText() const { + return QString(); +} + +QString MediaWebPage::pinnedTextSubstring() const { + return QString(); +} + +bool MediaWebPage::allowsEdit() const { + return false; +} + +bool MediaWebPage::updateInlineResultMedia(const MTPMessageMedia &media) { + return false; +} + +bool MediaWebPage::updateSentMedia(const MTPMessageMedia &media) { + return false; +} + +std::unique_ptr MediaWebPage::createView( + not_null message) { + return std::make_unique(message, _page); +} + +MediaGame::MediaGame( + not_null parent, + not_null game) +: Media(parent) +, _game(game) { +} + +std::unique_ptr MediaGame::clone(not_null parent) { + return std::make_unique(parent, _game); +} + +QString MediaGame::notificationText() const { + // Add a game controller emoji before game title. + auto result = QString(); + result.reserve(_game->title.size() + 3); + result.append( + QChar(0xD83C) + ).append( + QChar(0xDFAE) + ).append( + QChar(' ') + ).append(_game->title); + return result; +} + +GameData *MediaGame::game() const { + return _game; +} + +QString MediaGame::pinnedTextSubstring() const { + auto title = _game->title; + return lng_action_pinned_media_game(lt_game, title); +} + +QString MediaGame::errorTextForForward( + not_null channel) const { + if (channel->restricted(ChannelRestriction::f_send_games)) { + return lang(lng_restricted_send_inline); + } + return QString(); +} + +bool MediaGame::consumeMessageText(const TextWithEntities &text) { + _consumedText = text; + return true; +} + +bool MediaGame::updateInlineResultMedia(const MTPMessageMedia &media) { + return updateSentMedia(media); +} + +bool MediaGame::updateSentMedia(const MTPMessageMedia &media) { + if (media.type() != mtpc_messageMediaGame) { + return false; + } + const auto &game = media.c_messageMediaGame().vgame; + if (game.type() == mtpc_game) { + App::feedGame(game.c_game(), _game); + } + return true; +} + +std::unique_ptr MediaGame::createView( + not_null message) { + return std::make_unique(message, _game, _consumedText); +} + +MediaInvoice::MediaInvoice( + not_null parent, + const MTPDmessageMediaInvoice &data) +: Media(parent) +, _invoice(ComputeInvoiceData(data)) { +} + +MediaInvoice::MediaInvoice( + not_null parent, + const Invoice &data) +: Media(parent) +, _invoice(data) { +} + +std::unique_ptr MediaInvoice::clone(not_null parent) { + return std::make_unique(parent, _invoice); +} + +const Invoice *MediaInvoice::invoice() const { + return &_invoice; +} + +QString MediaInvoice::notificationText() const { + return _invoice.title; +} + +QString MediaInvoice::pinnedTextSubstring() const { + return QString(); +} + +bool MediaInvoice::updateInlineResultMedia(const MTPMessageMedia &media) { + return true; +} + +bool MediaInvoice::updateSentMedia(const MTPMessageMedia &media) { + return true; +} + +std::unique_ptr MediaInvoice::createView( + not_null message) { + return std::make_unique(message, &_invoice); +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h new file mode 100644 index 0000000000..7121a92333 --- /dev/null +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -0,0 +1,347 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class HistoryItem; +class HistoryMedia; + +namespace base { +template +class enum_mask; +} // namespace base + +namespace Storage { +enum class SharedMediaType : char; +using SharedMediaTypesMask = base::enum_mask; +} // namespace Storage + +namespace HistoryView { +enum class Context : char; +class Element; +} // namespace HistoryView + +namespace Data { + +enum class CallFinishReason : char { + Missed, + Busy, + Disconnected, + Hangup, +}; + +struct SharedContact { + UserId userId = 0; + QString firstName; + QString lastName; + QString phoneNumber; + +}; + +struct Call { + using FinishReason = CallFinishReason; + + int duration = 0; + FinishReason finishReason = FinishReason::Missed; + +}; + +struct Invoice { + MsgId receiptMsgId = 0; + uint64 amount = 0; + QString currency; + QString title; + QString description; + PhotoData *photo = nullptr; + bool isTest = false; + +}; + +class Media { +public: + Media(not_null parent); + virtual ~Media() = default; + + not_null parent() const; + + virtual std::unique_ptr clone(not_null parent) = 0; + + virtual DocumentData *document() const; + virtual PhotoData *photo() const; + virtual WebPageData *webpage() const; + virtual const SharedContact *sharedContact() const; + virtual const Call *call() const; + virtual GameData *game() const; + virtual const Invoice *invoice() const; + virtual LocationData *location() const; + + virtual bool uploading() const; + virtual Storage::SharedMediaTypesMask sharedMediaTypes() const; + virtual QString caption() const; + virtual bool hasReplyPreview() const; + virtual ImagePtr replyPreview() const; + // Returns text with link-start and link-end commands for service-color highlighting. + // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" + virtual QString chatsListText() const; + virtual QString notificationText() const = 0; + virtual QString pinnedTextSubstring() const = 0; + virtual bool allowsForward() const; + virtual bool allowsEdit() const; + virtual bool allowsEditCaption() const; + virtual bool allowsRevoke() const; + virtual bool forwardedBecomesUnread() const; + virtual QString errorTextForForward( + not_null channel) const; + + [[nodiscard]] virtual bool consumeMessageText( + const TextWithEntities &text); + + // After sending an inline result we may want to completely recreate + // the media (all media that was generated on client side, for example). + virtual bool updateInlineResultMedia(const MTPMessageMedia &media) = 0; + virtual bool updateSentMedia(const MTPMessageMedia &media) = 0; + virtual std::unique_ptr createView( + not_null message) = 0; + +private: + const not_null _parent; + +}; + +class MediaPhoto : public Media { +public: + MediaPhoto( + not_null parent, + not_null photo, + const QString &caption); + MediaPhoto( + not_null parent, + not_null chat, + not_null photo); + ~MediaPhoto(); + + std::unique_ptr clone(not_null parent) override; + + PhotoData *photo() const override; + + bool uploading() const override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; + QString caption() const override; + QString chatsListText() const override; + QString notificationText() const override; + QString pinnedTextSubstring() const override; + bool allowsEditCaption() const override; + QString errorTextForForward( + not_null channel) const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + +private: + not_null _photo; + PeerData *_chat = nullptr; + QString _caption; + +}; + +class MediaFile : public Media { +public: + MediaFile( + not_null parent, + not_null document, + const QString &caption); + ~MediaFile(); + + std::unique_ptr clone(not_null parent) override; + + DocumentData *document() const override; + + bool uploading() const override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; + QString chatsListText() const override; + QString notificationText() const override; + QString pinnedTextSubstring() const override; + bool allowsEditCaption() const override; + bool forwardedBecomesUnread() const override; + QString errorTextForForward( + not_null channel) const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + +private: + not_null _document; + QString _caption; + QString _emoji; + +}; + +class MediaContact : public Media { +public: + MediaContact( + not_null parent, + UserId userId, + const QString &firstName, + const QString &lastName, + const QString &phoneNumber); + + std::unique_ptr clone(not_null parent) override; + + const SharedContact *sharedContact() const override; + QString notificationText() const override; + QString pinnedTextSubstring() const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + +private: + SharedContact _contact; + +}; + +class MediaLocation : public Media { +public: + MediaLocation( + not_null parent, + const LocationCoords &coords); + MediaLocation( + not_null parent, + const LocationCoords &coords, + const QString &title, + const QString &description); + + std::unique_ptr clone(not_null parent) override; + + LocationData *location() const override; + QString chatsListText() const override; + QString notificationText() const override; + QString pinnedTextSubstring() const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + +private: + not_null _location; + QString _title; + QString _description; + +}; + +class MediaCall : public Media { +public: + MediaCall( + not_null parent, + const MTPDmessageActionPhoneCall &call); + + std::unique_ptr clone(not_null parent) override; + + const Call *call() const override; + QString notificationText() const override; + QString pinnedTextSubstring() const override; + bool allowsForward() const override; + bool allowsRevoke() const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + + static QString Text( + not_null item, + CallFinishReason reason); + +private: + Call _call; + +}; + +class MediaWebPage : public Media { +public: + MediaWebPage( + not_null parent, + not_null page); + + std::unique_ptr clone(not_null parent) override; + + WebPageData *webpage() const override; + QString notificationText() const override; + QString pinnedTextSubstring() const override; + bool allowsEdit() const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + +private: + not_null _page; + +}; + +class MediaGame : public Media { +public: + MediaGame( + not_null parent, + not_null game); + + std::unique_ptr clone(not_null parent) override; + + GameData *game() const override; + + QString notificationText() const override; + QString pinnedTextSubstring() const override; + QString errorTextForForward( + not_null channel) const override; + + bool consumeMessageText(const TextWithEntities &text) override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + +private: + not_null _game; + TextWithEntities _consumedText; + +}; + +class MediaInvoice : public Media { +public: + MediaInvoice( + not_null parent, + const MTPDmessageMediaInvoice &data); + MediaInvoice( + not_null parent, + const Invoice &data); + + std::unique_ptr clone(not_null parent) override; + + const Invoice *invoice() const override; + + QString notificationText() const override; + QString pinnedTextSubstring() const override; + + bool updateInlineResultMedia(const MTPMessageMedia &media) override; + bool updateSentMedia(const MTPMessageMedia &media) override; + std::unique_ptr createView( + not_null message) override; + +private: + Invoice _invoice; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index ca109aec7c..3bf4cab7a5 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -66,7 +66,7 @@ void PhotoData::notifyLayoutChanged() const { auto i = items.constFind(const_cast(this)); if (i != items.cend()) { for_const (auto item, i.value()) { - Auth().data().markItemLayoutChanged(item); + Auth().data().markItemLayoutChange(item); } } } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index ef2fb650a8..dddf57e789 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "observer_peer.h" #include "history/history_item_components.h" +#include "history/view/history_view_element.h" #include "data/data_feed.h" namespace Data { +using ViewElement = HistoryView::Element; + Session::Session() { Notify::PeerUpdateViewer( Notify::PeerUpdate::Flag::UserIsContact @@ -27,12 +30,61 @@ Session::Session() { Session::~Session() = default; -void Session::markItemLayoutChanged(not_null item) { - _itemLayoutChanged.fire_copy(item); +void Session::registerItemView(not_null view) { + _views[view->data()].push_back(view); +} + +void Session::unregisterItemView(not_null view) { + const auto i = _views.find(view->data()); + if (i != _views.end()) { + auto &list = i->second; + list.erase(ranges::remove(list, view), end(list)); + if (list.empty()) { + _views.erase(i); + } + } + if (App::hoveredItem() == view) { + App::hoveredItem(nullptr); + } + if (App::pressedItem() == view) { + App::pressedItem(nullptr); + } + if (App::hoveredLinkItem() == view) { + App::hoveredLinkItem(nullptr); + } + if (App::pressedLinkItem() == view) { + App::pressedLinkItem(nullptr); + } + if (App::mousedItem() == view) { + App::mousedItem(nullptr); + } +} + +void Session::markItemLayoutChange(not_null item) { + _itemLayoutChanges.fire_copy(item); } rpl::producer> Session::itemLayoutChanged() const { - return _itemLayoutChanged.events(); + return _itemLayoutChanges.events(); +} + +void Session::markViewLayoutChange(not_null view) { + _viewLayoutChanges.fire_copy(view); +} + +rpl::producer> Session::viewLayoutChanged() const { + return _viewLayoutChanges.events(); +} + +void Session::markItemIdChange(IdChange event) { + _itemIdChanges.fire_copy(event); + enumerateItemViews(event.item, [](not_null view) { + view->refreshDataId(); + }); +} + +rpl::producer Session::itemIdChanged() const { + return _itemIdChanges.events(); } void Session::requestItemRepaint(not_null item) { @@ -43,6 +95,14 @@ rpl::producer> Session::itemRepaintRequest() const return _itemRepaintRequest.events(); } +void Session::requestViewRepaint(not_null view) { + _viewRepaintRequest.fire_copy(view); +} + +rpl::producer> Session::viewRepaintRequest() const { + return _viewRepaintRequest.events(); +} + void Session::requestItemViewResize(not_null item) { _itemViewResizeRequest.fire_copy(item); } @@ -51,6 +111,14 @@ rpl::producer> Session::itemViewResizeRequest() con return _itemViewResizeRequest.events(); } +void Session::requestViewResize(not_null view) { + _viewResizeRequest.fire_copy(view); +} + +rpl::producer> Session::viewResizeRequest() const { + return _viewResizeRequest.events(); +} + void Session::requestItemViewRefresh(not_null item) { _itemViewRefreshRequest.fire_copy(item); } @@ -59,6 +127,14 @@ rpl::producer> Session::itemViewRefreshRequest() co return _itemViewRefreshRequest.events(); } +void Session::requestItemPlayInline(not_null item) { + _itemPlayInlineRequest.fire_copy(item); +} + +rpl::producer> Session::itemPlayInlineRequest() const { + return _itemPlayInlineRequest.events(); +} + void Session::markItemRemoved(not_null item) { _itemRemoved.fire_copy(item); } @@ -173,16 +249,9 @@ MessageIdsList Session::itemsToIds( }) | ranges::to_vector; } -MessageIdsList Session::groupToIds( - not_null group) const { - auto result = itemsToIds(group->others); - result.push_back(group->leader->fullId()); - return result; -} - MessageIdsList Session::itemOrItsGroup(not_null item) const { - if (const auto group = item->getFullGroup()) { - return groupToIds(group); + if (const auto group = groups().find(item)) { + return itemsToIds(group->items); } return { 1, item->fullId() }; } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index b776a290f6..d5ef5d0f12 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -9,8 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers.h" #include "dialogs/dialogs_key.h" +#include "data/data_groups.h" -struct HistoryMessageGroup; +class HistoryItem; + +namespace HistoryView { +struct Group; +class Element; +} // namespace HistoryView namespace Data { @@ -18,6 +24,8 @@ class Feed; class Session final { public: + using ViewElement = HistoryView::Element; + Session(); ~Session(); @@ -30,6 +38,7 @@ public: base::Observable &moreChatsLoaded() { return _moreChatsLoaded; } + base::Observable &pendingHistoryResize() { return _pendingHistoryResize; } @@ -40,18 +49,45 @@ public: base::Observable &queryItemVisibility() { return _queryItemVisibility; } - void markItemLayoutChanged(not_null item); + struct IdChange { + not_null item; + MsgId oldId = 0; + }; + + void registerItemView(not_null view); + void unregisterItemView(not_null view); + template + void enumerateItemViews(not_null item, Method method) { + if (const auto i = _views.find(item); i != _views.end()) { + for (const auto view : i->second) { + method(view); + } + } + } + + void markItemIdChange(IdChange event); + rpl::producer itemIdChanged() const; + void markItemLayoutChange(not_null item); rpl::producer> itemLayoutChanged() const; + void markViewLayoutChange(not_null view); + rpl::producer> viewLayoutChanged() const; void requestItemRepaint(not_null item); rpl::producer> itemRepaintRequest() const; + void requestViewRepaint(not_null view); + rpl::producer> viewRepaintRequest() const; void requestItemViewResize(not_null item); rpl::producer> itemViewResizeRequest() const; + void requestViewResize(not_null view); + rpl::producer> viewResizeRequest() const; void requestItemViewRefresh(not_null item); rpl::producer> itemViewRefreshRequest() const; + void requestItemPlayInline(not_null item); + rpl::producer> itemPlayInlineRequest() const; void markItemRemoved(not_null item); rpl::producer> itemRemoved() const; void markHistoryUnloaded(not_null history); rpl::producer> historyUnloaded() const; + void markHistoryCleared(not_null history); rpl::producer> historyCleared() const; using MegagroupParticipant = std::tuple< @@ -147,7 +183,6 @@ public: HistoryItemsList idsToItems(const MessageIdsList &ids) const; MessageIdsList itemsToIds(const HistoryItemsList &items) const; - MessageIdsList groupToIds(not_null group) const; MessageIdsList itemOrItsGroup(not_null item) const; int pinnedDialogsCount() const; @@ -165,6 +200,13 @@ public: void setMimeForwardIds(MessageIdsList &&list); MessageIdsList takeMimeForwardIds(); + Groups &groups() { + return _groups; + } + const Groups &groups() const { + return _groups; + } + private: bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const { constexpr auto kStickersUpdateTimeout = TimeMs(3600'000); @@ -181,10 +223,15 @@ private: base::Observable _moreChatsLoaded; base::Observable _pendingHistoryResize; base::Observable _queryItemVisibility; - rpl::event_stream> _itemLayoutChanged; + rpl::event_stream _itemIdChanges; + rpl::event_stream> _itemLayoutChanges; + rpl::event_stream> _viewLayoutChanges; rpl::event_stream> _itemRepaintRequest; + rpl::event_stream> _viewRepaintRequest; rpl::event_stream> _itemViewResizeRequest; + rpl::event_stream> _viewResizeRequest; rpl::event_stream> _itemViewRefreshRequest; + rpl::event_stream> _itemPlayInlineRequest; rpl::event_stream> _itemRemoved; rpl::event_stream> _historyUnloaded; rpl::event_stream> _historyCleared; @@ -207,6 +254,10 @@ private: std::deque _pinnedDialogs; base::flat_map> _feeds; + Groups _groups; + std::map< + not_null, + std::vector>> _views; MessageIdsList _mimeForwardIds; diff --git a/Telegram/SourceFiles/data/data_shared_media.cpp b/Telegram/SourceFiles/data/data_shared_media.cpp index 4493d13b43..f55c6a1650 100644 --- a/Telegram/SourceFiles/data/data_shared_media.cpp +++ b/Telegram/SourceFiles/data/data_shared_media.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/history_media_types.h" +#include "data/data_media_types.h" #include "data/data_sparse_ids.h" #include "info/info_memento.h" #include "info/info_controller.h" @@ -322,10 +323,8 @@ base::optional SharedMediaWithLastSlice::IsLastIsolated( } return LastFullMsgId(ending ? *ending : slice) | [](FullMsgId msgId) { return App::histItemById(msgId); } - | [](HistoryItem *item) { return item ? item->getMedia() : nullptr; } - | [](HistoryMedia *media) { - return media ? media->getPhoto() : nullptr; - } + | [](HistoryItem *item) { return item ? item->media() : nullptr; } + | [](Data::Media *media) { return media ? media->photo() : nullptr; } | [](PhotoData *photo) { return photo ? photo->id : 0; } | [&](PhotoId photoId) { return *lastPeerPhotoId != photoId; }; } diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 1410a67070..244babce46 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/value_ordering.h" + class HistoryItem; using HistoryItemsList = std::vector>; @@ -22,6 +24,32 @@ struct UploadState { } // namespace Data +struct MessageGroupId { + using Underlying = uint64; + + enum Type : Underlying { + None = 0, + } value; + + MessageGroupId(Type value = None) : value(value) { + } + static MessageGroupId FromRaw(Underlying value) { + return static_cast(value); + } + + explicit operator bool() const { + return value != None; + } + Underlying raw() const { + return static_cast(value); + } + + friend inline Type value_ordering_helper(MessageGroupId value) { + return value.value; + } + +}; + class PeerData; class UserData; class ChatData; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index d03281d195..ebba8739ad 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -243,9 +243,10 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) { if (auto main = App::main()) { InvokeQueued(main, [msgId] { if (auto item = App::histItemById(msgId)) { - if (auto media = item->getMedia()) { - media->playInline(true); - } + // #TODO GIFs + //if (auto media = item->getMedia()) { + // media->playInline(true); + //} } }); } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 036da17c2a..df5420546b 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "boxes/edit_participant_box.h" #include "data/data_session.h" +#include "data/data_media_types.h" namespace AdminLog { namespace { @@ -775,7 +776,7 @@ void InnerWidget::paintEmpty(Painter &p) { TextWithEntities InnerWidget::getSelectedText() const { return _selectedItem - ? _selectedItem->data()->selectedText(_selectedText) + ? _selectedItem->selectedText(_selectedText) : TextWithEntities(); } @@ -911,7 +912,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto item = view ? view->data().get() : nullptr; const auto itemId = item ? item->fullId() : FullMsgId(); bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); - bool canForward = item && item->canForward(); + bool canForward = item && item->allowsForward(); auto msg = dynamic_cast(item); if (isUponSelected > 0) { @@ -919,7 +920,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } else { if (item && !isUponSelected) { auto mediaHasTextForCopy = false; - if (auto media = (msg ? msg->getMedia() : nullptr)) { + if (auto media = view->media()) { mediaHasTextForCopy = media->hasTextForCopy(); if (media->type() == MediaTypeWebPage && static_cast(media)->attach()) { media = static_cast(media)->attach(); @@ -1040,8 +1041,8 @@ void InnerWidget::showContextInFolder(not_null document) { void InnerWidget::openContextGif(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (auto media = item->getMedia()) { - if (auto document = media->getDocument()) { + if (auto media = item->media()) { + if (auto document = media->document()) { Messenger::Instance().showDocument(document, item); } } @@ -1050,12 +1051,9 @@ void InnerWidget::openContextGif(FullMsgId itemId) { void InnerWidget::copyContextText(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (const auto media = item->getMedia()) { - if (media->type() == MediaTypeSticker) { - return; - } + if (const auto view = viewForItem(item)) { + setToClipboard(view->selectedText(FullSelection)); } - setToClipboard(item->selectedText(FullSelection)); } } @@ -1416,7 +1414,7 @@ void InnerWidget::updateSelected() { } auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; if (_mouseSelectType != TextSelectType::Letters) { - selection = _mouseActionItem->data()->adjustSelection( + selection = _mouseActionItem->adjustSelection( selection, _mouseSelectType); } @@ -1517,17 +1515,21 @@ void InnerWidget::performDrag() { // auto forwardMimeType = QString(); // auto pressedMedia = static_cast(nullptr); // if (auto pressedItem = App::pressedItem()) { - // pressedMedia = pressedItem->getMedia(); - // if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { + // pressedMedia = pressedItem->media(); + // if (_mouseCursorState == HistoryInDateCursorState + // || (pressedMedia && pressedMedia->dragItem())) { // forwardMimeType = qsl("application/x-td-forward"); - // Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem)); + // Auth().data().setMimeForwardIds( + // Auth().data().itemOrItsGroup(pressedItem->data())); // } // } // if (auto pressedLnkItem = App::pressedLinkItem()) { - // if ((pressedMedia = pressedLnkItem->getMedia())) { - // if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { + // if ((pressedMedia = pressedLnkItem->media())) { + // if (forwardMimeType.isEmpty() + // && pressedMedia->dragItemByHandler(pressedHandler)) { // forwardMimeType = qsl("application/x-td-forward"); - // Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() }); + // Auth().data().setMimeForwardIds( + // { 1, pressedLnkItem->fullId() }); // } // } // } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 8b36b43c6d..17c54a6559 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -422,9 +422,13 @@ void GenerateItems( auto text = lng_admin_log_pinned_message(lt_from, fromLinkText); addSimpleServiceMessage(text); - auto applyServiceAction = false; auto detachExistingItem = false; - addPart(history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); + addPart(history->createItem( + PrepareLogMessage( + action.vmessage, + idManager.next(), + date.v), + detachExistingItem)); } }; @@ -438,9 +442,13 @@ void GenerateItems( addSimpleServiceMessage(text); auto oldValue = ExtractEditedText(action.vprev_message); - auto applyServiceAction = false; auto detachExistingItem = false; - auto body = history->createItem(PrepareLogMessage(action.vnew_message, idManager.next(), date.v), applyServiceAction, detachExistingItem); + auto body = history->createItem( + PrepareLogMessage( + action.vnew_message, + idManager.next(), + date.v), + detachExistingItem); if (oldValue.text.isEmpty()) { oldValue = PrepareText(QString(), lang(lng_admin_log_empty_text)); } @@ -452,9 +460,10 @@ void GenerateItems( auto text = lng_admin_log_deleted_message(lt_from, fromLinkText); addSimpleServiceMessage(text); - auto applyServiceAction = false; auto detachExistingItem = false; - addPart(history->createItem(PrepareLogMessage(action.vmessage, idManager.next(), date.v), applyServiceAction, detachExistingItem)); + addPart(history->createItem( + PrepareLogMessage(action.vmessage, idManager.next(), date.v), + detachExistingItem)); }; auto createParticipantJoin = [&]() { diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index efe6facd44..6c53d55d54 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -49,14 +49,6 @@ constexpr auto kStatusShowClientsidePlayGame = 10000; constexpr auto kSetMyActionForMs = 10000; constexpr auto kNewBlockEachMessage = 50; -HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from) { - auto text = TextWithEntities { lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")) }; - TextUtilities::ParseEntities(text, Ui::ItemTextNoMonoOptions().flags); - text.entities.push_front(EntityInText(EntityInTextItalic, 0, text.text.size())); - flags &= ~MTPDmessage::Flag::f_post_author; - return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, QString(), text); -} - } // namespace History::History(const PeerId &peerId) @@ -729,324 +721,128 @@ void Histories::checkSelfDestructItems() { } HistoryItem *History::createItem( - const MTPMessage &msg, - bool applyServiceAction, + const MTPMessage &message, bool detachExistingItem) { - const auto msgId = idFromMessage(msg); - if (!msgId) return nullptr; + const auto messageId = idFromMessage(message); + if (!messageId) { + return nullptr; + } - auto result = App::histItemById(channelId(), msgId); - if (result) { + if (const auto result = App::histItemById(channelId(), messageId)) { if (detachExistingItem) { result->removeMainView(); } - if (msg.type() == mtpc_message) { - const auto media = msg.c_message().has_media() - ? &msg.c_message().vmedia + if (message.type() == mtpc_message) { + const auto media = message.c_message().has_media() + ? &message.c_message().vmedia : nullptr; result->updateMedia(media); - if (applyServiceAction) { - App::checkSavedGif(result); - } } return result; } - - switch (msg.type()) { - case mtpc_messageEmpty: { - auto message = HistoryService::PreparedText { lang(lng_message_empty) }; - result = HistoryService::create(this, msg.c_messageEmpty().vid.v, date(), message); - } break; - - case mtpc_message: { - auto &m = msg.c_message(); - enum class MediaCheckResult { - Good, - Unsupported, - Empty, - HasTimeToLive, - }; - auto badMedia = MediaCheckResult::Good; - if (m.has_media()) switch (m.vmedia.type()) { - case mtpc_messageMediaEmpty: - case mtpc_messageMediaContact: break; - case mtpc_messageMediaGeo: - switch (m.vmedia.c_messageMediaGeo().vgeo.type()) { - case mtpc_geoPoint: break; - case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; - default: badMedia = MediaCheckResult::Unsupported; break; - } - break; - case mtpc_messageMediaVenue: - switch (m.vmedia.c_messageMediaVenue().vgeo.type()) { - case mtpc_geoPoint: break; - case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; - default: badMedia = MediaCheckResult::Unsupported; break; - } - break; - case mtpc_messageMediaGeoLive: - switch (m.vmedia.c_messageMediaGeoLive().vgeo.type()) { - case mtpc_geoPoint: break; - case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; - default: badMedia = MediaCheckResult::Unsupported; break; - } - break; - case mtpc_messageMediaPhoto: { - auto &photo = m.vmedia.c_messageMediaPhoto(); - if (photo.has_ttl_seconds()) { - badMedia = MediaCheckResult::HasTimeToLive; - } else if (!photo.has_photo()) { - badMedia = MediaCheckResult::Empty; - } else { - switch (photo.vphoto.type()) { - case mtpc_photo: break; - case mtpc_photoEmpty: badMedia = MediaCheckResult::Empty; break; - default: badMedia = MediaCheckResult::Unsupported; break; - } - } - } break; - case mtpc_messageMediaDocument: { - auto &document = m.vmedia.c_messageMediaDocument(); - if (document.has_ttl_seconds()) { - badMedia = MediaCheckResult::HasTimeToLive; - } else if (!document.has_document()) { - badMedia = MediaCheckResult::Empty; - } else { - switch (document.vdocument.type()) { - case mtpc_document: break; - case mtpc_documentEmpty: badMedia = MediaCheckResult::Empty; break; - default: badMedia = MediaCheckResult::Unsupported; break; - } - } - } break; - case mtpc_messageMediaWebPage: - switch (m.vmedia.c_messageMediaWebPage().vwebpage.type()) { - case mtpc_webPage: - case mtpc_webPageEmpty: - case mtpc_webPagePending: break; - case mtpc_webPageNotModified: - default: badMedia = MediaCheckResult::Unsupported; break; - } - break; - case mtpc_messageMediaGame: - switch (m.vmedia.c_messageMediaGame().vgame.type()) { - case mtpc_game: break; - default: badMedia = MediaCheckResult::Unsupported; break; - } - break; - case mtpc_messageMediaInvoice: - break; - case mtpc_messageMediaUnsupported: - default: badMedia = MediaCheckResult::Unsupported; break; - } - if (badMedia == MediaCheckResult::Unsupported) { - result = createUnsupportedMessage(this, m.vid.v, m.vflags.v, m.vreply_to_msg_id.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v); - } else if (badMedia == MediaCheckResult::Empty) { - auto message = HistoryService::PreparedText { lang(lng_message_empty) }; - result = HistoryService::create(this, m.vid.v, date(m.vdate), message, m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0); - } else if (badMedia == MediaCheckResult::HasTimeToLive) { - result = HistoryService::create(this, m); - } else { - result = HistoryMessage::create(this, m); - } - } break; - - case mtpc_messageService: { - auto &m = msg.c_messageService(); - if (m.vaction.type() == mtpc_messageActionPhoneCall) { - result = HistoryMessage::create(this, m); - } else { - result = HistoryService::create(this, m); - } - - if (applyServiceAction) { - auto &action = m.vaction; - switch (action.type()) { - case mtpc_messageActionChatAddUser: { - auto &d = action.c_messageActionChatAddUser(); - if (auto megagroup = peer->asMegagroup()) { - auto mgInfo = megagroup->mgInfo.get(); - Assert(mgInfo != nullptr); - auto &v = d.vusers.v; - for (auto i = 0, l = v.size(); i != l; ++i) { - if (auto user = App::userLoaded(peerFromUser(v[i]))) { - if (!base::contains(mgInfo->lastParticipants, user)) { - mgInfo->lastParticipants.push_front(user); - Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); - Auth().data().addNewMegagroupParticipant(megagroup, user); - } - if (user->botInfo) { - peer->asChannel()->mgInfo->bots.insert(user); - if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) { - peer->asChannel()->mgInfo->botStatus = 2; - } - } - } - } - } - } break; - - case mtpc_messageActionChatJoinedByLink: { - auto &d = action.c_messageActionChatJoinedByLink(); - if (auto megagroup = peer->asMegagroup()) { - auto mgInfo = megagroup->mgInfo.get(); - Assert(mgInfo != nullptr); - if (auto user = result->from()->asUser()) { - if (!base::contains(mgInfo->lastParticipants, user)) { - mgInfo->lastParticipants.push_front(user); - Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); - Auth().data().addNewMegagroupParticipant(megagroup, user); - } - if (user->botInfo) { - mgInfo->bots.insert(user); - if (mgInfo->botStatus != 0 && mgInfo->botStatus < 2) { - mgInfo->botStatus = 2; - } - } - } - } - } break; - - case mtpc_messageActionChatDeletePhoto: { - if (const auto chat = peer->asChat()) { - chat->setPhoto(MTP_chatPhotoEmpty()); - } - } break; - - case mtpc_messageActionChatDeleteUser: { - auto &d = action.c_messageActionChatDeleteUser(); - auto uid = peerFromUser(d.vuser_id); - if (lastKeyboardFrom == uid) { - clearLastKeyboard(); - } - if (auto megagroup = peer->asMegagroup()) { - if (auto user = App::userLoaded(uid)) { - auto mgInfo = megagroup->mgInfo.get(); - Assert(mgInfo != nullptr); - auto i = ranges::find( - mgInfo->lastParticipants, - user, - [](not_null user) { return user.get(); }); - if (i != mgInfo->lastParticipants.end()) { - mgInfo->lastParticipants.erase(i); - Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); - } - Auth().data().removeMegagroupParticipant(megagroup, user); - if (megagroup->membersCount() > 1) { - megagroup->setMembersCount(megagroup->membersCount() - 1); - } else { - mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated; - mgInfo->lastParticipantsCount = 0; - } - if (mgInfo->lastAdmins.contains(user)) { - mgInfo->lastAdmins.remove(user); - if (megagroup->adminsCount() > 1) { - megagroup->setAdminsCount(megagroup->adminsCount() - 1); - } - Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::AdminsChanged); - } - mgInfo->bots.remove(user); - if (mgInfo->bots.empty() && mgInfo->botStatus > 0) { - mgInfo->botStatus = -1; - } - } - Data::ChannelAdminChanges(megagroup).feed(uid, false); - } - } break; - - case mtpc_messageActionChatEditPhoto: { - auto &d = action.c_messageActionChatEditPhoto(); - if (d.vphoto.type() == mtpc_photo) { - auto &sizes = d.vphoto.c_photo().vsizes.v; - if (!sizes.isEmpty()) { - auto photo = App::feedPhoto(d.vphoto.c_photo()); - if (photo) photo->peer = peer; - auto &smallSize = sizes.front(); - auto &bigSize = sizes.back(); - const MTPFileLocation *smallLoc = 0, *bigLoc = 0; - switch (smallSize.type()) { - case mtpc_photoSize: smallLoc = &smallSize.c_photoSize().vlocation; break; - case mtpc_photoCachedSize: smallLoc = &smallSize.c_photoCachedSize().vlocation; break; - } - switch (bigSize.type()) { - case mtpc_photoSize: bigLoc = &bigSize.c_photoSize().vlocation; break; - case mtpc_photoCachedSize: bigLoc = &bigSize.c_photoCachedSize().vlocation; break; - } - if (smallLoc && bigLoc) { - const auto newPhotoId = photo ? photo->id : 0; - if (const auto chat = peer->asChat()) { - chat->setPhoto(newPhotoId, MTP_chatPhoto(*smallLoc, *bigLoc)); - } else if (const auto channel = peer->asChannel()) { - channel->setPhoto(newPhotoId, MTP_chatPhoto(*smallLoc, *bigLoc)); - } - peer->loadUserpic(); - } - } - } - } break; - - case mtpc_messageActionChatEditTitle: { - auto &d = action.c_messageActionChatEditTitle(); - if (auto chat = peer->asChat()) { - chat->setName(qs(d.vtitle)); - } - } break; - - case mtpc_messageActionChatMigrateTo: { - if (auto chat = peer->asChat()) { - chat->addFlags(MTPDchat::Flag::f_deactivated); - } - //auto &d = action.c_messageActionChatMigrateTo(); - //auto channel = App::channelLoaded(d.vchannel_id.v); - } break; - - case mtpc_messageActionChannelMigrateFrom: { - //auto &d = action.c_messageActionChannelMigrateFrom(); - //auto chat = App::chatLoaded(d.vchat_id.v); - } break; - - case mtpc_messageActionPinMessage: { - if (m.has_reply_to_msg_id() && result) { - if (auto channel = result->history()->peer->asChannel()) { - channel->setPinnedMessageId(m.vreply_to_msg_id.v); - } - } - } break; - - case mtpc_messageActionPhoneCall: { - Calls::Current().newServiceMessage().notify(result->fullId()); - } break; - } - } - } break; - } - - if (applyServiceAction) { - App::checkSavedGif(result); - } - - return result; + return HistoryItem::Create(this, message); } -not_null History::createItemForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, UserId from, const QString &postAuthor, HistoryMessage *msg) { - return HistoryMessage::create(this, id, flags, date, from, postAuthor, msg); +not_null History::createItemForwarded( + MsgId id, + MTPDmessage::Flags flags, + QDateTime date, + UserId from, + const QString &postAuthor, + HistoryMessage *original) { + return HistoryMessage::create( + this, + id, + flags, + date, + from, + postAuthor, + original); } -not_null History::createItemDocument(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup) { - return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, postAuthor, doc, caption, markup); +not_null History::createItemDocument( + MsgId id, + MTPDmessage::Flags flags, + UserId viaBotId, + MsgId replyTo, + QDateTime date, + UserId from, + const QString &postAuthor, + DocumentData *document, + const QString &caption, + const MTPReplyMarkup &markup) { + return HistoryMessage::create( + this, + id, + flags, + replyTo, + viaBotId, + date, + from, + postAuthor, + document, + caption, + markup); } -not_null History::createItemPhoto(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup) { - return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, postAuthor, photo, caption, markup); +not_null History::createItemPhoto( + MsgId id, + MTPDmessage::Flags flags, + UserId viaBotId, + MsgId replyTo, + QDateTime date, + UserId from, + const QString &postAuthor, + PhotoData *photo, + const QString &caption, + const MTPReplyMarkup &markup) { + return HistoryMessage::create( + this, + id, + flags, + replyTo, + viaBotId, + date, + from, + postAuthor, + photo, + caption, + markup); } -not_null History::createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup) { - return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, postAuthor, game, markup); +not_null History::createItemGame( + MsgId id, + MTPDmessage::Flags flags, + UserId viaBotId, + MsgId replyTo, + QDateTime date, + UserId from, + const QString &postAuthor, + GameData *game, + const MTPReplyMarkup &markup) { + return HistoryMessage::create( + this, + id, + flags, + replyTo, + viaBotId, + date, + from, + postAuthor, + game, + markup); } -not_null History::addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags, bool newMsg) { +not_null History::addNewService( + MsgId msgId, + QDateTime date, + const QString &text, + MTPDmessage::Flags flags, + bool unread) { auto message = HistoryService::PreparedText { text }; - return addNewItem(HistoryService::create(this, msgId, date, message, flags), newMsg); + return addNewItem( + HistoryService::create(this, msgId, date, message, flags), + unread); } HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type) { @@ -1072,16 +868,16 @@ HistoryItem *History::addNewToLastBlock( NewMessageType type) { Expects(type != NewMessageExisting); - const auto applyServiceAction = (type == NewMessageUnread); const auto detachExistingItem = (type != NewMessageLast); - const auto item = createItem( - msg, - applyServiceAction, - detachExistingItem); + const auto item = createItem(msg, detachExistingItem); if (!item || item->mainView()) { return item; } - const auto result = addNewItem(item, (type == NewMessageUnread)); + const auto newUnreadMessage = (type == NewMessageUnread); + if (newUnreadMessage) { + applyMessageChanges(item, msg); + } + const auto result = addNewItem(item, newUnreadMessage); if (type == NewMessageLast) { // When we add just one last item, like we do while loading dialogs, // we want to remove a single added grouped media, otherwise it will @@ -1096,23 +892,96 @@ HistoryItem *History::addNewToLastBlock( } HistoryItem *History::addToHistory(const MTPMessage &msg) { - return createItem(msg, false, false); + const auto detachExistingItem = false; + return createItem(msg, detachExistingItem); } -not_null History::addNewForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, UserId from, const QString &postAuthor, HistoryMessage *item) { - return addNewItem(createItemForwarded(id, flags, date, from, postAuthor, item), true); +not_null History::addNewForwarded( + MsgId id, + MTPDmessage::Flags flags, + QDateTime date, + UserId from, + const QString &postAuthor, + HistoryMessage *original) { + return addNewItem( + createItemForwarded(id, flags, date, from, postAuthor, original), + true); } -not_null History::addNewDocument(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup) { - return addNewItem(createItemDocument(id, flags, viaBotId, replyTo, date, from, postAuthor, doc, caption, markup), true); +not_null History::addNewDocument( + MsgId id, + MTPDmessage::Flags flags, + UserId viaBotId, + MsgId replyTo, + QDateTime date, + UserId from, + const QString &postAuthor, + DocumentData *document, + const QString &caption, + const MTPReplyMarkup &markup) { + return addNewItem( + createItemDocument( + id, + flags, + viaBotId, + replyTo, + date, + from, + postAuthor, + document, + caption, + markup), + true); } -not_null History::addNewPhoto(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup) { - return addNewItem(createItemPhoto(id, flags, viaBotId, replyTo, date, from, postAuthor, photo, caption, markup), true); +not_null History::addNewPhoto( + MsgId id, + MTPDmessage::Flags flags, + UserId viaBotId, + MsgId replyTo, + QDateTime date, + UserId from, + const QString &postAuthor, + PhotoData *photo, + const QString &caption, + const MTPReplyMarkup &markup) { + return addNewItem( + createItemPhoto( + id, + flags, + viaBotId, + replyTo, + date, + from, + postAuthor, + photo, + caption, + markup), + true); } -not_null History::addNewGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup) { - return addNewItem(createItemGame(id, flags, viaBotId, replyTo, date, from, postAuthor, game, markup), true); +not_null History::addNewGame( + MsgId id, + MTPDmessage::Flags flags, + UserId viaBotId, + MsgId replyTo, + QDateTime date, + UserId from, + const QString &postAuthor, + GameData *game, + const MTPReplyMarkup &markup) { + return addNewItem( + createItemGame( + id, + flags, + viaBotId, + replyTo, + date, + from, + postAuthor, + game, + markup), + true); } void History::setUnreadMentionsCount(int count) { @@ -1205,28 +1074,30 @@ void History::addUnreadMentionsSlice(const MTPmessages_Messages &result) { Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged); } -not_null History::addNewItem(not_null adding, bool newMsg) { +not_null History::addNewItem( + not_null item, + bool unread) { Expects(!isBuildingFrontBlock()); - addItemToBlock(adding); + addItemToBlock(item); - const auto [groupFrom, groupTill] = recountGroupingFromTill(adding); - if (groupFrom != groupTill || groupFrom->groupId()) { + const auto [groupFrom, groupTill] = recountGroupingFromTill(item->mainView()); + if (groupFrom != groupTill || groupFrom->data()->groupId()) { recountGrouping(groupFrom, groupTill); } - if (!newMsg && IsServerMsgId(adding->id)) { - if (const auto sharedMediaTypes = adding->sharedMediaTypes()) { + if (!unread && IsServerMsgId(item->id)) { + if (const auto sharedMediaTypes = item->sharedMediaTypes()) { auto from = loadedAtTop() ? 0 : minMsgId(); auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId(); Auth().storage().add(Storage::SharedMediaAddExisting( peer->id, sharedMediaTypes, - adding->id, + item->id, { from, till })); } } - if (adding->from()->id) { - if (auto user = adding->from()->asUser()) { + if (item->from()->id) { + if (auto user = item->from()->asUser()) { auto getLastAuthors = [this]() -> std::deque>* { if (auto chat = peer->asChat()) { return &chat->lastAuthors; @@ -1267,9 +1138,10 @@ not_null History::addNewItem(not_null adding, bool n } } } - if (adding->definesReplyKeyboard()) { - auto markupFlags = adding->replyKeyboardFlags(); - if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective) || adding->mentionsMe()) { + if (item->definesReplyKeyboard()) { + auto markupFlags = item->replyKeyboardFlags(); + if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective) + || item->mentionsMe()) { auto getMarkupSenders = [this]() -> base::flat_set>* { if (auto chat = peer->asChat()) { return &chat->markupSenders; @@ -1279,25 +1151,37 @@ not_null History::addNewItem(not_null adding, bool n return nullptr; }; if (auto markupSenders = getMarkupSenders()) { - markupSenders->insert(adding->from()); + markupSenders->insert(item->from()); } if (markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_zero) { // zero markup means replyKeyboardHide - if (lastKeyboardFrom == adding->from()->id || (!lastKeyboardInited && !peer->isChat() && !peer->isMegagroup() && !adding->out())) { + if (lastKeyboardFrom == item->from()->id + || (!lastKeyboardInited + && !peer->isChat() + && !peer->isMegagroup() + && !item->out())) { clearLastKeyboard(); } } else { bool botNotInChat = false; if (peer->isChat()) { - botNotInChat = adding->from()->isUser() && (!peer->canWrite() || !peer->asChat()->participants.empty()) && !peer->asChat()->participants.contains(adding->from()->asUser()); + botNotInChat = item->from()->isUser() + && (!peer->asChat()->participants.empty() + || !peer->canWrite()) + && !peer->asChat()->participants.contains( + item->from()->asUser()); } else if (peer->isMegagroup()) { - botNotInChat = adding->from()->isUser() && (!peer->canWrite() || peer->asChannel()->mgInfo->botStatus != 0) && !peer->asChannel()->mgInfo->bots.contains(adding->from()->asUser()); + botNotInChat = item->from()->isUser() + && (peer->asChannel()->mgInfo->botStatus != 0 + || !peer->canWrite()) + && !peer->asChannel()->mgInfo->bots.contains( + item->from()->asUser()); } if (botNotInChat) { clearLastKeyboard(); } else { lastKeyboardInited = true; - lastKeyboardId = adding->id; - lastKeyboardFrom = adding->from()->id; + lastKeyboardId = item->id; + lastKeyboardFrom = item->from()->id; lastKeyboardUsed = false; } } @@ -1305,12 +1189,183 @@ not_null History::addNewItem(not_null adding, bool n } } - setLastMessage(adding); - if (newMsg) { - newItemAdded(adding); + setLastMessage(item); + if (unread) { + newItemAdded(item); } - return adding; + return item; +} + +void History::applyMessageChanges( + not_null item, + const MTPMessage &data) { + if (data.type() == mtpc_messageService) { + applyServiceChanges(item, data.c_messageService()); + } + App::checkSavedGif(item); +} + +void History::applyServiceChanges( + not_null item, + const MTPDmessageService &data) { + auto &action = data.vaction; + switch (action.type()) { + case mtpc_messageActionChatAddUser: { + auto &d = action.c_messageActionChatAddUser(); + if (auto megagroup = peer->asMegagroup()) { + auto mgInfo = megagroup->mgInfo.get(); + Assert(mgInfo != nullptr); + auto &v = d.vusers.v; + for (auto i = 0, l = v.size(); i != l; ++i) { + if (auto user = App::userLoaded(peerFromUser(v[i]))) { + if (!base::contains(mgInfo->lastParticipants, user)) { + mgInfo->lastParticipants.push_front(user); + Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); + Auth().data().addNewMegagroupParticipant(megagroup, user); + } + if (user->botInfo) { + peer->asChannel()->mgInfo->bots.insert(user); + if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) { + peer->asChannel()->mgInfo->botStatus = 2; + } + } + } + } + } + } break; + + case mtpc_messageActionChatJoinedByLink: { + auto &d = action.c_messageActionChatJoinedByLink(); + if (auto megagroup = peer->asMegagroup()) { + auto mgInfo = megagroup->mgInfo.get(); + Assert(mgInfo != nullptr); + if (auto user = item->from()->asUser()) { + if (!base::contains(mgInfo->lastParticipants, user)) { + mgInfo->lastParticipants.push_front(user); + Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); + Auth().data().addNewMegagroupParticipant(megagroup, user); + } + if (user->botInfo) { + mgInfo->bots.insert(user); + if (mgInfo->botStatus != 0 && mgInfo->botStatus < 2) { + mgInfo->botStatus = 2; + } + } + } + } + } break; + + case mtpc_messageActionChatDeletePhoto: { + if (const auto chat = peer->asChat()) { + chat->setPhoto(MTP_chatPhotoEmpty()); + } + } break; + + case mtpc_messageActionChatDeleteUser: { + auto &d = action.c_messageActionChatDeleteUser(); + auto uid = peerFromUser(d.vuser_id); + if (lastKeyboardFrom == uid) { + clearLastKeyboard(); + } + if (auto megagroup = peer->asMegagroup()) { + if (auto user = App::userLoaded(uid)) { + auto mgInfo = megagroup->mgInfo.get(); + Assert(mgInfo != nullptr); + auto i = ranges::find( + mgInfo->lastParticipants, + user, + [](not_null user) { return user.get(); }); + if (i != mgInfo->lastParticipants.end()) { + mgInfo->lastParticipants.erase(i); + Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); + } + Auth().data().removeMegagroupParticipant(megagroup, user); + if (megagroup->membersCount() > 1) { + megagroup->setMembersCount(megagroup->membersCount() - 1); + } else { + mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated; + mgInfo->lastParticipantsCount = 0; + } + if (mgInfo->lastAdmins.contains(user)) { + mgInfo->lastAdmins.remove(user); + if (megagroup->adminsCount() > 1) { + megagroup->setAdminsCount(megagroup->adminsCount() - 1); + } + Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::AdminsChanged); + } + mgInfo->bots.remove(user); + if (mgInfo->bots.empty() && mgInfo->botStatus > 0) { + mgInfo->botStatus = -1; + } + } + Data::ChannelAdminChanges(megagroup).feed(uid, false); + } + } break; + + case mtpc_messageActionChatEditPhoto: { + auto &d = action.c_messageActionChatEditPhoto(); + if (d.vphoto.type() == mtpc_photo) { + auto &sizes = d.vphoto.c_photo().vsizes.v; + if (!sizes.isEmpty()) { + auto photo = App::feedPhoto(d.vphoto.c_photo()); + if (photo) photo->peer = peer; + auto &smallSize = sizes.front(); + auto &bigSize = sizes.back(); + const MTPFileLocation *smallLoc = 0, *bigLoc = 0; + switch (smallSize.type()) { + case mtpc_photoSize: smallLoc = &smallSize.c_photoSize().vlocation; break; + case mtpc_photoCachedSize: smallLoc = &smallSize.c_photoCachedSize().vlocation; break; + } + switch (bigSize.type()) { + case mtpc_photoSize: bigLoc = &bigSize.c_photoSize().vlocation; break; + case mtpc_photoCachedSize: bigLoc = &bigSize.c_photoCachedSize().vlocation; break; + } + if (smallLoc && bigLoc) { + const auto newPhotoId = photo ? photo->id : 0; + if (const auto chat = peer->asChat()) { + chat->setPhoto(newPhotoId, MTP_chatPhoto(*smallLoc, *bigLoc)); + } else if (const auto channel = peer->asChannel()) { + channel->setPhoto(newPhotoId, MTP_chatPhoto(*smallLoc, *bigLoc)); + } + peer->loadUserpic(); + } + } + } + } break; + + case mtpc_messageActionChatEditTitle: { + auto &d = action.c_messageActionChatEditTitle(); + if (auto chat = peer->asChat()) { + chat->setName(qs(d.vtitle)); + } + } break; + + case mtpc_messageActionChatMigrateTo: { + if (auto chat = peer->asChat()) { + chat->addFlags(MTPDchat::Flag::f_deactivated); + } + //auto &d = action.c_messageActionChatMigrateTo(); + //auto channel = App::channelLoaded(d.vchannel_id.v); + } break; + + case mtpc_messageActionChannelMigrateFrom: { + //auto &d = action.c_messageActionChannelMigrateFrom(); + //auto chat = App::chatLoaded(d.vchat_id.v); + } break; + + case mtpc_messageActionPinMessage: { + if (data.has_reply_to_msg_id() && item) { + if (auto channel = item->history()->peer->asChannel()) { + channel->setPinnedMessageId(data.vreply_to_msg_id.v); + } + } + } break; + + case mtpc_messageActionPhoneCall: { + Calls::Current().newServiceMessage().notify(item->fullId()); + } break; + } } void History::clearSendAction(not_null from) { @@ -1444,7 +1499,8 @@ void History::addOlderSlice(const QVector &slice) { for (auto i = slice.cend(), e = slice.cbegin(); i != e;) { --i; - const auto adding = createItem(*i, false, true); + const auto detachExistingItem = true; + const auto adding = createItem(*i, detachExistingItem); if (!adding) continue; if (!firstAdded) firstAdded = adding; @@ -1547,8 +1603,8 @@ void History::addOlderSlice(const QVector &slice) { CrashReports::ClearAnnotation("old_minmaxwas_minmaxadd"); if (lastAdded) { - const auto [from, till] = recountGroupingFromTill(lastAdded); - recountGrouping(firstAdded, till); + const auto [from, till] = recountGroupingFromTill(lastAdded->mainView()); + recountGrouping(firstAdded->mainView(), till); } if (isChannel()) { @@ -1583,7 +1639,8 @@ void History::addNewerSlice(const QVector &slice) { std::vector medias[Storage::kSharedMediaTypeCount]; for (auto i = slice.cend(), e = slice.cbegin(); i != e;) { --i; - const auto adding = createItem(*i, false, true); + const auto detachExistingItem = true; + const auto adding = createItem(*i, detachExistingItem); if (!adding) continue; if (!firstAdded) firstAdded = adding; @@ -1627,8 +1684,8 @@ void History::addNewerSlice(const QVector &slice) { } if (firstAdded) { - const auto [from, till] = recountGroupingFromTill(firstAdded); - recountGrouping(from, lastAdded); + const auto [from, till] = recountGroupingFromTill(firstAdded->mainView()); + recountGrouping(from, lastAdded->mainView()); } if (isChannel()) asChannelHistory()->checkJoinedMessage(); @@ -1956,7 +2013,7 @@ void History::destroyUnreadBar() { } not_null History::addNewInTheMiddle( - not_null newItem, + not_null item, int blockIndex, int itemIndex) { Expects(blockIndex >= 0); @@ -1968,7 +2025,7 @@ not_null History::addNewInTheMiddle( const auto it = block->messages.insert( block->messages.begin() + itemIndex, - newItem->createView( + item->createView( App::wnd()->controller(), HistoryView::Context::History)); (*it)->attachToBlock(block.get(), itemIndex); @@ -1984,49 +2041,47 @@ not_null History::addNewInTheMiddle( (*it)->nextInBlocksChanged(); } - const auto [groupFrom, groupTill] = recountGroupingFromTill(newItem); - if (groupFrom != groupTill || groupFrom->groupId()) { + const auto [groupFrom, groupTill] = recountGroupingFromTill(item->mainView()); + if (groupFrom != groupTill || groupFrom->data()->groupId()) { recountGrouping(groupFrom, groupTill); } - return newItem; + return item; } -HistoryItem *History::findNextItem(not_null item) const { - Expects(item->mainView()); - - const auto nextBlockIndex = item->mainView()->block()->indexInHistory() + 1; - const auto nextItemIndex = item->mainView()->indexInBlock() + 1; - if (nextItemIndex < int(item->mainView()->block()->messages.size())) { - return item->mainView()->block()->messages[nextItemIndex]->data(); +HistoryView::Element *History::findNextItem( + not_null view) const { + const auto nextBlockIndex = view->block()->indexInHistory() + 1; + const auto nextItemIndex = view->indexInBlock() + 1; + if (nextItemIndex < int(view->block()->messages.size())) { + return view->block()->messages[nextItemIndex].get(); } else if (nextBlockIndex < int(blocks.size())) { - return blocks[nextBlockIndex]->messages.front()->data(); + return blocks[nextBlockIndex]->messages.front().get(); } return nullptr; } -HistoryItem *History::findPreviousItem(not_null item) const { - Expects(item->mainView()); - - const auto blockIndex = item->mainView()->block()->indexInHistory(); - const auto itemIndex = item->mainView()->indexInBlock(); +HistoryView::Element *History::findPreviousItem( + not_null view) const { + const auto blockIndex = view->block()->indexInHistory(); + const auto itemIndex = view->indexInBlock(); if (itemIndex > 0) { - return item->mainView()->block()->messages[itemIndex - 1]->data(); + return view->block()->messages[itemIndex - 1].get(); } else if (blockIndex > 0) { - return blocks[blockIndex - 1]->messages.back()->data(); + return blocks[blockIndex - 1]->messages.back().get(); } return nullptr; } -not_null History::findGroupFirst( - not_null item) const { - const auto group = item->Get(); +not_null History::findGroupFirst( + not_null view) const { + const auto group = view->Get(); Assert(group != nullptr); Assert(group->leader != nullptr); - const auto leaderGroup = (group->leader == item) + const auto leaderGroup = (group->leader == view) ? group - : group->leader->Get(); + : group->leader->Get(); Assert(leaderGroup != nullptr); return leaderGroup->others.empty() @@ -2034,9 +2089,9 @@ not_null History::findGroupFirst( : leaderGroup->others.front().get(); } -not_null History::findGroupLast( - not_null item) const { - const auto group = item->Get(); +not_null History::findGroupLast( + not_null view) const { + const auto group = view->Get(); Assert(group != nullptr); return group->leader; @@ -2046,7 +2101,7 @@ void History::recountGroupingAround(not_null item) { Expects(item->history() == this); if (item->mainView() && item->groupId()) { - const auto [groupFrom, groupTill] = recountGroupingFromTill(item); + const auto [groupFrom, groupTill] = recountGroupingFromTill(item->mainView()); recountGrouping(groupFrom, groupTill); } } @@ -2081,44 +2136,41 @@ void History::paintUserpic( peer->paintUserpic(p, x, y, size); } -auto History::recountGroupingFromTill(not_null item) --> std::pair, not_null> { +auto History::recountGroupingFromTill(not_null view) +-> std::pair, not_null> { const auto recountFromItem = [&] { - if (const auto prev = findPreviousItem(item)) { - if (prev->groupId()) { + if (const auto prev = findPreviousItem(view)) { + if (prev->data()->groupId()) { return findGroupFirst(prev); } } - return item; + return view; }(); - if (recountFromItem == item && !item->groupId()) { - return { item, item }; + if (recountFromItem == view && !view->data()->groupId()) { + return { view, view }; } const auto recountTillItem = [&] { - if (const auto next = findNextItem(item)) { - if (next->groupId()) { + if (const auto next = findNextItem(view)) { + if (next->data()->groupId()) { return findGroupLast(next); } } - return item; + return view; }(); return { recountFromItem, recountTillItem }; } void History::recountGrouping( - not_null from, - not_null till) { - Expects(from->mainView()); - Expects(till->mainView()); - + not_null from, + not_null till) { from->validateGroupId(); - auto others = std::vector>(); - auto currentGroupId = from->groupId(); + auto others = std::vector>(); + auto currentGroupId = from->data()->groupId(); auto prev = from; while (prev != till) { auto item = findNextItem(prev); item->validateGroupId(); - const auto groupId = item->groupId(); + const auto groupId = item->data()->groupId(); if (currentGroupId) { if (groupId == currentGroupId) { others.push_back(prev); @@ -2369,7 +2421,8 @@ bool History::hasOrphanMediaGroupPart() const { } else if (blocks.front()->messages.size() != 1) { return false; } - return blocks.front()->messages.front()->data()->groupId() != MessageGroupId(); + const auto last = blocks.front()->messages.front()->data(); + return last->groupId() != MessageGroupId(); } bool History::removeOrphanMediaGroupPart() { @@ -2568,19 +2621,19 @@ void HistoryBlock::clear(bool leaveItems) { void HistoryBlock::remove(not_null view) { Expects(view->block() == this); - const auto item = view->data(); - auto [groupFrom, groupTill] = _history->recountGroupingFromTill(item); + auto [groupFrom, groupTill] = _history->recountGroupingFromTill(view); const auto groupHistory = _history; const auto needGroupRecount = (groupFrom != groupTill); if (needGroupRecount) { - if (groupFrom == item) { + if (groupFrom == view) { groupFrom = groupHistory->findNextItem(groupFrom); } - if (groupTill == item) { + if (groupTill == view) { groupTill = groupHistory->findPreviousItem(groupTill); } } + const auto item = view->data(); auto blockIndex = indexInHistory(); auto itemIndex = view->indexInBlock(); if (_history->showFrom == item) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 690a500af5..12c7b75a8b 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -164,7 +164,9 @@ public: not_null addNewGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup); // Used only internally and for channel admin log. - HistoryItem *createItem(const MTPMessage &msg, bool applyServiceAction, bool detachExistingItem); + HistoryItem *createItem( + const MTPMessage &message, + bool detachExistingItem); void addOlderSlice(const QVector &slice); void addNewerSlice(const QVector &slice); @@ -400,9 +402,11 @@ protected: not_null createItemPhoto(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); not_null createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup); - not_null addNewItem(not_null adding, bool newMsg); + not_null addNewItem( + not_null item, + bool unread); not_null addNewInTheMiddle( - not_null newItem, + not_null item, int blockIndex, int itemIndex); @@ -429,6 +433,13 @@ private: void changedInChatListHook(Dialogs::Mode list, bool added) override; void changedChatListPinHook() override; + void applyMessageChanges( + not_null item, + const MTPMessage &original); + void applyServiceChanges( + not_null item, + const MTPDmessageService &data); + // After adding a new history slice check the lastMsg and newLoaded. void checkLastMsg(); @@ -441,17 +452,19 @@ private: void clearSendAction(not_null from); - HistoryItem *findPreviousItem(not_null item) const; - HistoryItem *findNextItem(not_null item) const; - not_null findGroupFirst( - not_null item) const; - not_null findGroupLast( - not_null item) const; - auto recountGroupingFromTill(not_null item) - -> std::pair, not_null>; + HistoryView::Element *findPreviousItem( + not_null view) const; + HistoryView::Element *findNextItem( + not_null view) const; + not_null findGroupFirst( + not_null view) const; + not_null findGroupLast( + not_null view) const; + auto recountGroupingFromTill(not_null view) + -> std::pair, not_null>; void recountGrouping( - not_null from, - not_null till); + not_null from, + not_null till); enum class Flag { f_has_pending_resized_items = (1 << 0), diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index feaf634ab6..57fd50d10d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "lang/lang_keys.h" #include "data/data_session.h" +#include "data/data_media_types.h" namespace { @@ -405,7 +406,7 @@ void HistoryInner::enumerateDates(Method method) { TextSelection HistoryInner::computeRenderSelection( not_null selected, - not_null item) const { + not_null view) const { const auto itemSelection = [&](not_null item) { auto i = selected->find(item); if (i != selected->end()) { @@ -413,22 +414,22 @@ TextSelection HistoryInner::computeRenderSelection( } return TextSelection(); }; - const auto group = item->Get(); + const auto group = view->Get(); if (group) { - if (group->leader != item) { + if (group->leader != view) { return TextSelection(); } auto result = TextSelection(); auto allFullSelected = true; const auto count = int(group->others.size()); for (auto i = 0; i != count; ++i) { - if (itemSelection(group->others[i]) == FullSelection) { + if (itemSelection(group->others[i]->data()) == FullSelection) { result = AddGroupItemSelection(result, i); } else { allFullSelected = false; } } - const auto leaderSelection = itemSelection(item); + const auto leaderSelection = itemSelection(view->data()); if (leaderSelection == FullSelection) { return allFullSelected ? FullSelection @@ -438,7 +439,7 @@ TextSelection HistoryInner::computeRenderSelection( } return result; } - return itemSelection(item); + return itemSelection(view->data()); } TextSelection HistoryInner::itemRenderSelection( @@ -452,7 +453,7 @@ TextSelection HistoryInner::itemRenderSelection( return FullSelection; } } else if (!_selected.empty()) { - return computeRenderSelection(&_selected, item); + return computeRenderSelection(&_selected, view); } return TextSelection(); } @@ -528,7 +529,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { } if (item->mentionsMe() && item->isMediaUnread()) { readMentions.insert(item); - _widget->enqueueMessageHighlight(item); + _widget->enqueueMessageHighlight(view); } int32 h = view->height(); @@ -574,7 +575,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { } if (item->mentionsMe() && item->isMediaUnread()) { readMentions.insert(item); - _widget->enqueueMessageHighlight(item); + _widget->enqueueMessageHighlight(view); } } p.translate(0, h); @@ -984,7 +985,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but if (uponSelected) { _mouseAction = MouseAction::PrepareDrag; // start text drag } else if (!_pressWasInactive) { - if (dynamic_cast(App::pressedItem()->data()->getMedia()) || _mouseCursorState == HistoryInDateCursorState) { + if (dynamic_cast(App::pressedItem()->media()) || _mouseCursorState == HistoryInDateCursorState) { _mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag } else { if (dragState.afterSymbol) ++_mouseTextSymbol; @@ -1089,17 +1090,23 @@ void HistoryInner::performDrag() { auto forwardMimeType = QString(); auto pressedMedia = static_cast(nullptr); if (auto pressedItem = App::pressedItem()) { - pressedMedia = pressedItem->data()->getMedia(); - if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { - Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem->data())); + pressedMedia = pressedItem->media(); + if (_mouseCursorState == HistoryInDateCursorState + || (pressedMedia && pressedMedia->dragItem())) { + Auth().data().setMimeForwardIds( + Auth().data().itemOrItsGroup(pressedItem->data())); forwardMimeType = qsl("application/x-td-forward"); } } if (const auto pressedLnkItem = _mouseActionItem) { - if ((pressedMedia = pressedLnkItem->getMedia())) { - if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { - Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() }); - forwardMimeType = qsl("application/x-td-forward"); + if (const auto view = pressedLnkItem->mainView()) { + if ((pressedMedia = view->media())) { + if (forwardMimeType.isEmpty() + && pressedMedia->dragItemByHandler(pressedHandler)) { + Auth().data().setMimeForwardIds( + { 1, pressedLnkItem->fullId() }); + forwardMimeType = qsl("application/x-td-forward"); + } } } } @@ -1162,9 +1169,11 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu // if we are in selecting items mode perhaps we want to // toggle selection instead of activating the pressed link if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) { - if (auto media = _mouseActionItem->getMedia()) { - if (media->toggleSelectionByHandlerClick(activated)) { - activated = nullptr; + if (const auto view = _mouseActionItem->mainView()) { + if (const auto media = view->media()) { + if (media->toggleSelectionByHandlerClick(activated)) { + activated = nullptr; + } } } } @@ -1232,7 +1241,11 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { const auto [item, selection] = *_selected.cbegin(); - setToClipboard(item->selectedText(selection), QClipboard::Selection); + if (const auto view = item->mainView()) { + setToClipboard( + view->selectedText(selection), + QClipboard::Selection); + } } #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } @@ -1340,7 +1353,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _widget->replyToMessage(itemId); }); } - if (item->canEdit(::date(unixtime()))) { + if (item->allowsEdit(::date(unixtime()))) { _menu->addAction(lang(lng_context_edit_msg), [=] { _widget->editMessage(itemId); }); @@ -1423,7 +1436,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } else if (item) { const auto itemId = item->fullId(); if (isUponSelected != -2) { - if (item->canForward()) { + if (item->allowsForward()) { _menu->addAction(lang(lng_context_forward_msg), [=] { forwardItem(itemId); })->setEnabled(true); @@ -1453,10 +1466,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { : App::hoveredLinkItem() ? App::hoveredLinkItem()->data().get() : nullptr) { - if (const auto group = result->getFullGroup()) { - return group->others.empty() - ? group->leader - : group->others.front().get(); + if (const auto group = Auth().data().groups().find(result)) { + return group->items.front(); } return result; } @@ -1466,8 +1477,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); - const auto canForward = item - && item->canForward(); + const auto canForward = item && item->allowsForward(); + const auto view = item ? item->mainView() : nullptr; const auto msg = dynamic_cast(item); if (isUponSelected > 0) { @@ -1477,7 +1488,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { addItemActions(item); if (item && !isUponSelected) { auto mediaHasTextForCopy = false; - if (auto media = (msg ? msg->getMedia() : nullptr)) { + if (auto media = (view ? view->media() : nullptr)) { mediaHasTextForCopy = media->hasTextForCopy(); if (media->type() == MediaTypeWebPage && static_cast(media)->attach()) { media = static_cast(media)->attach(); @@ -1665,8 +1676,8 @@ void HistoryInner::saveDocumentToFile(not_null document) { void HistoryInner::openContextGif(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (const auto media = item->getMedia()) { - if (const auto document = media->getDocument()) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { Messenger::Instance().showDocument(document, item); } } @@ -1675,8 +1686,8 @@ void HistoryInner::openContextGif(FullMsgId itemId) { void HistoryInner::saveContextGif(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (const auto media = item->getMedia()) { - if (const auto document = media->getDocument()) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { _widget->saveGif(document); } } @@ -1685,14 +1696,11 @@ void HistoryInner::saveContextGif(FullMsgId itemId) { void HistoryInner::copyContextText(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (const auto media = item->getMedia()) { - if (media->type() == MediaTypeSticker) { - return; - } + if (const auto view = item->mainView()) { + const auto group = view->getFullGroup(); + const auto leader = group ? group->leader : view; + setToClipboard(leader->selectedText(FullSelection)); } - const auto group = item->getFullGroup(); - const auto leader = group ? group->leader : item; - setToClipboard(leader->selectedText(FullSelection)); } } @@ -1718,26 +1726,30 @@ TextWithEntities HistoryInner::getSelectedText() const { } if (selected.cbegin()->second != FullSelection) { const auto [item, selection] = *selected.cbegin(); - return item->selectedText(selection); + if (const auto view = item->mainView()) { + return view->selectedText(selection); + } + return TextWithEntities(); } const auto timeFormat = qsl(", [dd.MM.yy hh:mm]\n"); - auto groupLeadersAdded = base::flat_set>(); + auto groupLeadersAdded = base::flat_set>(); auto fullSize = 0; auto texts = base::flat_map, TextWithEntities>(); const auto addItem = [&]( - not_null item, + not_null view, TextSelection selection) { + const auto item = view->data(); auto time = item->date.toString(timeFormat); auto part = TextWithEntities(); - auto unwrapped = item->selectedText(selection); + auto unwrapped = view->selectedText(selection); auto size = item->author()->name.size() + time.size() + unwrapped.text.size(); part.text.reserve(size); - auto y = itemTop(item); + auto y = itemTop(view); if (y >= 0) { part.text.append(item->author()->name).append(time); TextUtilities::Append(part, std::move(unwrapped)); @@ -1747,11 +1759,12 @@ TextWithEntities HistoryInner::getSelectedText() const { }; for (const auto [item, selection] : selected) { - if (!item->mainView()) { + const auto view = item->mainView(); + if (!view) { continue; } - if (const auto group = item->Get()) { + if (const auto group = view->getFullGroup()) { if (groupLeadersAdded.contains(group->leader)) { continue; } @@ -1761,16 +1774,16 @@ TextWithEntities HistoryInner::getSelectedText() const { if (leaderSelection == FullSelection) { groupLeadersAdded.emplace(group->leader); addItem(group->leader, FullSelection); - } else if (item == group->leader) { + } else if (view == group->leader) { const auto leaderFullSelection = AddGroupItemSelection( TextSelection(), int(group->others.size())); - addItem(item, leaderFullSelection); + addItem(view, leaderFullSelection); } else { - addItem(item, FullSelection); + addItem(view, FullSelection); } } else { - addItem(item, FullSelection); + addItem(view, FullSelection); } } @@ -2174,7 +2187,7 @@ auto HistoryInner::getSelectionState() const if (selected.first->canDelete()) { ++result.canDeleteCount; } - if (selected.first->canForward()) { + if (selected.first->allowsForward()) { ++result.canForwardCount; } } else { @@ -2411,7 +2424,9 @@ void HistoryInner::onUpdateSelected() { } auto selState = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; if (_mouseSelectType != TextSelectType::Letters) { - selState = _mouseActionItem->adjustSelection(selState, _mouseSelectType); + if (const auto view = _mouseActionItem->mainView()) { + selState = view->adjustSelection(selState, _mouseSelectType); + } } if (_selected[_mouseActionItem] != selState) { _selected[_mouseActionItem] = selState; @@ -2570,7 +2585,7 @@ int HistoryInner::itemTop(const HistoryItem *item) const { } int HistoryInner::itemTop(const Element *view) const { - if (!view) { + if (!view || view->data()->mainView() != view) { return -1; } @@ -2609,10 +2624,12 @@ int HistoryInner::moveScrollFollowingInlineKeyboard( const HistoryItem *item, int oldKeyboardTop, int newKeyboardTop) { - if (item->isUnderCursor()) { - const auto top = itemTop(item); - if (top >= oldKeyboardTop) { - return newKeyboardTop - oldKeyboardTop; + if (const auto view = item ? item->mainView() : nullptr) { + if (view->isUnderCursor()) { + const auto top = itemTop(item); + if (top >= oldKeyboardTop) { + return newKeyboardTop - oldKeyboardTop; + } } } return 0; @@ -2632,11 +2649,8 @@ bool HistoryInner::isSelected( bool HistoryInner::isSelectedAsGroup( not_null toItems, not_null item) const { - if (const auto group = item->getFullGroup()) { - if (!isSelected(toItems, group->leader)) { - return false; - } - for (const auto other : group->others) { + if (const auto group = Auth().data().groups().find(item)) { + for (const auto other : group->items) { if (!isSelected(toItems, other)) { return false; } @@ -2703,7 +2717,7 @@ void HistoryInner::changeSelectionAsGroup( not_null toItems, not_null item, SelectAction action) const { - const auto group = item->getFullGroup(); + const auto group = Auth().data().groups().find(item); if (!group) { return changeSelection(toItems, item, action); } @@ -2716,10 +2730,7 @@ void HistoryInner::changeSelectionAsGroup( const auto add = (action == SelectAction::Select); const auto adding = [&] { - if (!add || !goodForSelection(toItems, group->leader, total)) { - return false; - } - for (const auto other : group->others) { + for (const auto other : group->items) { if (!goodForSelection(toItems, other, total)) { return false; } @@ -2727,13 +2738,11 @@ void HistoryInner::changeSelectionAsGroup( return (total <= MaxSelectedItems); }(); if (adding) { - addToSelection(toItems, group->leader); - for (const auto other : group->others) { + for (const auto other : group->items) { addToSelection(toItems, other); } } else { - removeFromSelection(toItems, group->leader); - for (const auto other : group->others) { + for (const auto other : group->items) { removeFromSelection(toItems, other); } } @@ -2745,13 +2754,7 @@ void HistoryInner::forwardItem(FullMsgId itemId) { void HistoryInner::forwardAsGroup(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (const auto group = item->getFullGroup()) { - auto items = Auth().data().itemsToIds(group->others); - items.push_back(group->leader->fullId()); - Window::ShowForwardMessagesBox(std::move(items)); - } else { - Window::ShowForwardMessagesBox({ 1, itemId }); - } + Window::ShowForwardMessagesBox(Auth().data().itemOrItsGroup(item)); } } @@ -2779,13 +2782,12 @@ bool HistoryInner::hasPendingResizedItems() const { void HistoryInner::deleteAsGroup(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - const auto group = item->getFullGroup(); - if (!group || group->others.empty()) { + const auto group = Auth().data().groups().find(item); + if (!group || group->items.size() < 2) { return deleteItem(item); } - auto items = Auth().data().itemsToIds(group->others); - items.push_back(group->leader->fullId()); - Ui::show(Box(Auth().data().groupToIds(group))); + Ui::show(Box( + Auth().data().itemsToIds(group->items))); } } @@ -2866,7 +2868,7 @@ QString HistoryInner::tooltipText() const { if (const auto view = App::hoveredItem()) { auto dateText = view->data()->date.toString( QLocale::system().dateTimeFormat(QLocale::LongFormat)); - auto editedDate = view->data()->displayedEditDate(); + auto editedDate = view->displayedEditDate(); if (!editedDate.isNull()) { dateText += '\n' + lng_edited_date(lt_date, editedDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat))); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 3409cf2fe9..220490db55 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -212,7 +212,7 @@ private: int seltoy) const; TextSelection computeRenderSelection( not_null selected, - not_null item) const; + not_null view) const; void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 9ec5cf4b66..f5293a7f68 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "history/history_media_types.h" #include "history/history_media_grouped.h" +#include "history/history_service.h" #include "history/history_message.h" #include "history/history.h" #include "media/media_clip_reader.h" @@ -34,25 +35,40 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "data/data_session.h" #include "data/data_messages.h" +#include "data/data_media_types.h" #include "data/data_feed.h" -namespace internal { +namespace { -TextSelection unshiftSelection(TextSelection selection, uint16 byLength) { - if (selection == FullSelection) { - return selection; - } - return ::unshiftSelection(selection, byLength); +not_null CreateUnsupportedMessage( + not_null history, + MsgId msgId, + MTPDmessage::Flags flags, + MsgId replyTo, + UserId viaBotId, + QDateTime date, + UserId from) { + const auto siteLink = qsl("https://desktop.telegram.org"); + auto text = TextWithEntities{ + lng_message_unsupported(lt_link, siteLink) + }; + TextUtilities::ParseEntities(text, Ui::ItemTextNoMonoOptions().flags); + text.entities.push_front( + EntityInText(EntityInTextItalic, 0, text.text.size())); + flags &= ~MTPDmessage::Flag::f_post_author; + return HistoryMessage::create( + history, + msgId, + flags, + replyTo, + viaBotId, + date, + from, + QString(), + text); } -TextSelection shiftSelection(TextSelection selection, uint16 byLength) { - if (selection == FullSelection) { - return selection; - } - return ::shiftSelection(selection, byLength); -} - -} // namespace internal +} // namespace HistoryItem::HistoryItem( not_null history, @@ -93,6 +109,12 @@ void HistoryItem::finishEdition(int oldKeyboardTop) { App::historyUpdateDependent(this); } +void HistoryItem::setGroupId(MessageGroupId groupId) { + Auth().data().groups().unregisterMessage(this); + _groupId = groupId; + Auth().data().groups().registerMessage(this); +} + HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() { if (const auto markup = Get()) { if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) { @@ -189,12 +211,17 @@ MTPDreplyKeyboardMarkup::Flags HistoryItem::replyKeyboardFlags() const { return MTPDreplyKeyboardMarkup_ClientFlag::f_zero | 0; } -void HistoryItem::addLogEntryOriginal(WebPageId localId, const QString &label, const TextWithEntities &content) { +void HistoryItem::addLogEntryOriginal( + WebPageId localId, + const QString &label, + const TextWithEntities &content) { Expects(isLogEntry()); + AddComponents(HistoryMessageLogEntryOriginal::Bit()); - auto original = Get(); - auto webpage = App::feedWebPage(localId, label, content); - original->_page = std::make_unique(this, webpage); + Get()->page = App::feedWebPage( + localId, + label, + content); } UserData *HistoryItem::viaBot() const { @@ -276,20 +303,11 @@ void HistoryItem::removeMainView() { void HistoryItem::clearMainView() { _mainView = nullptr; - - validateGroupId(); - if (groupId()) { - makeGroupLeader({}); - } } void HistoryItem::addToUnreadMentions(UnreadMentionType type) { } -Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const { - return {}; -} - void HistoryItem::indexAsNewItem() { if (IsServerMsgId(id)) { CrashReports::SetAnnotation("addToUnreadMentions", QString::number(id)); @@ -307,27 +325,21 @@ void HistoryItem::indexAsNewItem() { void HistoryItem::setRealId(MsgId newId) { Expects(!IsServerMsgId(id)); - id = newId; + App::historyUnregItem(this); + const auto oldId = std::exchange(id, newId); + App::historyRegItem(this); // We don't need to call Notify::replyMarkupUpdated(this) and update keyboard // in history widget, because it can't exist for an outgoing message. // Only inline keyboards can be in outgoing messages. - if (auto markup = inlineReplyMarkup()) { + if (const auto markup = inlineReplyMarkup()) { if (markup->inlineKeyboard) { markup->inlineKeyboard->updateMessageId(); } } - if (_media) { - _media->refreshParentId(this); - if (const auto group = Get()) { - if (group->leader != this) { - if (const auto media = group->leader->getMedia()) { - media->refreshParentId(group->leader); - } - } - } - } + Auth().data().markItemIdChange({ this, oldId }); + Auth().data().requestItemRepaint(this); } bool HistoryItem::isPinned() const { @@ -347,60 +359,11 @@ bool HistoryItem::canPin() const { return false; } -bool HistoryItem::canForward() const { - if (id < 0 || isLogEntry()) { - return false; - } - if (auto message = toHistoryMessage()) { - if (auto media = message->getMedia()) { - if (media->type() == MediaTypeCall) { - return false; - } - } - return true; - } +bool HistoryItem::allowsForward() const { return false; } -bool HistoryItem::canEdit(const QDateTime &cur) const { - auto messageToMyself = _history->peer->isSelf(); - auto canPinInMegagroup = [&] { - if (auto megagroup = _history->peer->asMegagroup()) { - return megagroup->canPinMessages(); - } - return false; - }(); - auto messageTooOld = (messageToMyself || canPinInMegagroup) - ? false - : (date.secsTo(cur) >= Global::EditTimeLimit()); - if (id < 0 || messageTooOld) { - return false; - } - - if (auto message = toHistoryMessage()) { - if (message->Has() || message->Has()) { - return false; - } - - if (auto media = message->getMedia()) { - if (!media->canEditCaption() && media->type() != MediaTypeWebPage) { - return false; - } - } - if (messageToMyself) { - return true; - } - if (auto channel = _history->peer->asChannel()) { - if (isPost() && channel->canEditMessages()) { - return true; - } - if (out()) { - return !isPost() || channel->canPublish(); - } - } else { - return out(); - } - } +bool HistoryItem::allowsEdit(const QDateTime &now) const { return false; } @@ -443,8 +406,8 @@ bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const { if (!toHistoryMessage()) { return false; } - if (auto media = getMedia()) { - if (media->type() == MediaTypeCall) { + if (const auto media = this->media()) { + if (!media->allowsRevoke()) { return false; } } @@ -493,8 +456,8 @@ QString HistoryItem::directLink() const { Assert(channel != nullptr); auto query = channel->username + '/' + QString::number(id); if (!channel->isMegagroup()) { - if (auto media = getMedia()) { - if (auto document = media->getDocument()) { + if (const auto media = this->media()) { + if (const auto document = media->document()) { if (document->isVideoMessage()) { return qsl("https://telesco.pe/") + query; } @@ -565,13 +528,6 @@ MsgId HistoryItem::idOriginal() const { return id; } -bool HistoryItem::hasOutLayout() const { - if (history()->peer->isSelf()) { - return !Has(); - } - return out() && !isPost(); -} - bool HistoryItem::needCheck() const { return out() || (id < 0 && history()->peer->isSelf()); } @@ -658,84 +614,7 @@ void HistoryItem::setUnreadBarFreezed() { } MessageGroupId HistoryItem::groupId() const { - if (const auto group = Get()) { - return group->groupId; - } - return MessageGroupId::None; -} - -bool HistoryItem::groupIdValidityChanged() { - if (Has()) { - if (_media && _media->canBeGrouped()) { - return false; - } - RemoveComponents(HistoryMessageGroup::Bit()); - Auth().data().requestItemViewResize(this); - return true; - } - return false; -} - -void HistoryItem::makeGroupMember(not_null leader) { - Expects(leader != this); - - const auto group = Get(); - Assert(group != nullptr); - if (group->leader == this) { - if (auto single = _media ? _media->takeLastFromGroup() : nullptr) { - _media = std::move(single); - } - _flags |= MTPDmessage_ClientFlag::f_hidden_by_group; - Auth().data().requestItemViewResize(this); - - group->leader = leader; - base::take(group->others); - } else if (group->leader != leader) { - group->leader = leader; - } - - Ensures(isHiddenByGroup()); - Ensures(group->others.empty()); -} - -void HistoryItem::makeGroupLeader( - std::vector> &&others) { - const auto group = Get(); - Assert(group != nullptr); - - const auto leaderChanged = (group->leader != this); - if (leaderChanged) { - group->leader = this; - _flags &= ~MTPDmessage_ClientFlag::f_hidden_by_group; - Auth().data().requestItemViewResize(this); - } - group->others = std::move(others); - if (!_media || !_media->applyGroup(group->others)) { - resetGroupMedia(group->others); - invalidateChatsListEntry(); - } - - Ensures(!isHiddenByGroup()); -} - -HistoryMessageGroup *HistoryItem::getFullGroup() { - if (const auto group = Get()) { - if (group->leader == this) { - return group; - } - return group->leader->Get(); - } - return nullptr; -} - -void HistoryItem::resetGroupMedia( - const std::vector> &others) { - if (!others.empty()) { - _media = std::make_unique(this, others); - } else if (_media) { - _media = _media->takeLastFromGroup(); - } - Auth().data().requestItemViewResize(this); + return _groupId; } int HistoryItem::displayedDateHeight() const { @@ -758,79 +637,74 @@ bool HistoryItem::isEmpty() const { void HistoryItem::clipCallback(Media::Clip::Notification notification) { using namespace Media::Clip; - auto media = getMedia(); + auto media = this->media(); if (!media) { return; } - auto reader = media->getClipReader(); - if (!reader) { - return; - } + // #TODO GIFs + //auto reader = media->getClipReader(); + //if (!reader) { + // return; + //} - switch (notification) { - case NotificationReinit: { - auto stopped = false; - if (reader->autoPausedGif()) { - auto amVisible = false; - Auth().data().queryItemVisibility().notify({ this, &amVisible }, true); - if (!amVisible) { // stop animation if it is not visible - media->stopInline(); - if (auto document = media->getDocument()) { // forget data from memory - document->forget(); - } - stopped = true; - } - } else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) { - // Stop finished video message. - media->stopInline(); - } - if (!stopped) { - Auth().data().requestItemViewResize(this); - Auth().data().markItemLayoutChanged(this); - Global::RefPendingRepaintItems().insert(this); - } - } break; + //switch (notification) { + //case NotificationReinit: { + // auto stopped = false; + // if (reader->autoPausedGif()) { + // auto amVisible = false; + // Auth().data().queryItemVisibility().notify({ this, &amVisible }, true); + // if (!amVisible) { // stop animation if it is not visible + // media->stopInline(); + // if (auto document = media->getDocument()) { // forget data from memory + // document->forget(); + // } + // stopped = true; + // } + // } else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) { + // // Stop finished video message. + // media->stopInline(); + // } + // if (!stopped) { + // Auth().data().requestItemViewResize(this); + // Auth().data().markItemLayoutChange(this); + // Global::RefPendingRepaintItems().insert(this); + // } + //} break; - case NotificationRepaint: { - if (!reader->currentDisplayed()) { - Auth().data().requestItemRepaint(this); - } - } break; - } + //case NotificationRepaint: { + // if (!reader->currentDisplayed()) { + // Auth().data().requestItemRepaint(this); + // } + //} break; + //} } void HistoryItem::audioTrackUpdated() { - auto media = getMedia(); + auto media = this->media(); if (!media) { return; } - auto reader = media->getClipReader(); - if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) { - return; - } + // #TODO GIFs + //auto reader = media->getClipReader(); + //if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) { + // return; + //} - auto audio = reader->audioMsgId(); - auto current = Media::Player::mixer()->currentState(audio.type()); - if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) { - media->stopInline(); - } else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) { - if (!reader->videoPaused()) { - reader->pauseResumeVideo(); - } - } else { - if (reader->videoPaused()) { - reader->pauseResumeVideo(); - } - } -} - -bool HistoryItem::isUnderCursor() const { - if (const auto view = App::hoveredItem()) { - return view->data() == this; - } - return false; + //auto audio = reader->audioMsgId(); + //auto current = Media::Player::mixer()->currentState(audio.type()); + //if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) { + // media->stopInline(); + //} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) { + // if (!reader->videoPaused()) { + // reader->pauseResumeVideo(); + // } + //} else { + // if (reader->videoPaused()) { + // reader->pauseResumeVideo(); + // } + //} } HistoryItem *HistoryItem::previousItem() const { @@ -869,7 +743,7 @@ QString HistoryItem::notificationText() const { QString HistoryItem::inDialogsText(DrawInDialog way) const { auto getText = [this]() { if (emptyText()) { - return _media ? _media->inDialogsText() : QString(); + return _media ? _media->chatsListText() : QString(); } return TextUtilities::Clean(_text.originalText()); }; @@ -879,7 +753,7 @@ QString HistoryItem::inDialogsText(DrawInDialog way) const { return nullptr; } else if (!_history->peer->isUser() || out()) { return author(); - } else if (_history->peer->isSelf() && !hasOutLayout()) { + } else if (_history->peer->isSelf() && !Has()) { return senderOriginal(); } return nullptr; @@ -914,6 +788,7 @@ void HistoryItem::drawInDialog( } HistoryItem::~HistoryItem() { + Auth().data().groups().unregisterMessage(this); App::historyUnregItem(this); if (id < 0 && !App::quitting()) { Auth().uploader().cancel(fullId()); @@ -940,3 +815,135 @@ ClickHandlerPtr goToMessageClickHandler( ClickHandlerPtr goToMessageClickHandler(not_null item) { return goToMessageClickHandler(item->history()->peer, item->id); } + +not_null HistoryItem::Create( + not_null history, + const MTPMessage &message) { + switch (message.type()) { + case mtpc_messageEmpty: { + const auto &data = message.c_messageEmpty(); + const auto text = HistoryService::PreparedText { + lang(lng_message_empty) + }; + return HistoryService::create(history, data.vid.v, ::date(), text); + } break; + + case mtpc_message: { + const auto &data = message.c_message(); + enum class MediaCheckResult { + Good, + Unsupported, + Empty, + HasTimeToLive, + }; + auto badMedia = MediaCheckResult::Good; + const auto &media = data.vmedia; + if (data.has_media()) switch (media.type()) { + case mtpc_messageMediaEmpty: + case mtpc_messageMediaContact: break; + case mtpc_messageMediaGeo: + switch (media.c_messageMediaGeo().vgeo.type()) { + case mtpc_geoPoint: break; + case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; + } + break; + case mtpc_messageMediaVenue: + switch (media.c_messageMediaVenue().vgeo.type()) { + case mtpc_geoPoint: break; + case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; + } + break; + case mtpc_messageMediaGeoLive: + switch (media.c_messageMediaGeoLive().vgeo.type()) { + case mtpc_geoPoint: break; + case mtpc_geoPointEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; + } + break; + case mtpc_messageMediaPhoto: { + auto &photo = media.c_messageMediaPhoto(); + if (photo.has_ttl_seconds()) { + badMedia = MediaCheckResult::HasTimeToLive; + } else if (!photo.has_photo()) { + badMedia = MediaCheckResult::Empty; + } else { + switch (photo.vphoto.type()) { + case mtpc_photo: break; + case mtpc_photoEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; + } + } + } break; + case mtpc_messageMediaDocument: { + auto &document = media.c_messageMediaDocument(); + if (document.has_ttl_seconds()) { + badMedia = MediaCheckResult::HasTimeToLive; + } else if (!document.has_document()) { + badMedia = MediaCheckResult::Empty; + } else { + switch (document.vdocument.type()) { + case mtpc_document: break; + case mtpc_documentEmpty: badMedia = MediaCheckResult::Empty; break; + default: badMedia = MediaCheckResult::Unsupported; break; + } + } + } break; + case mtpc_messageMediaWebPage: + switch (media.c_messageMediaWebPage().vwebpage.type()) { + case mtpc_webPage: + case mtpc_webPageEmpty: + case mtpc_webPagePending: break; + case mtpc_webPageNotModified: + default: badMedia = MediaCheckResult::Unsupported; break; + } + break; + case mtpc_messageMediaGame: + switch (media.c_messageMediaGame().vgame.type()) { + case mtpc_game: break; + default: badMedia = MediaCheckResult::Unsupported; break; + } + break; + case mtpc_messageMediaInvoice: + break; + case mtpc_messageMediaUnsupported: + default: badMedia = MediaCheckResult::Unsupported; break; + } + if (badMedia == MediaCheckResult::Unsupported) { + return CreateUnsupportedMessage( + history, + data.vid.v, + data.vflags.v, + data.vreply_to_msg_id.v, + data.vvia_bot_id.v, + ::date(data.vdate), + data.vfrom_id.v); + } else if (badMedia == MediaCheckResult::Empty) { + const auto text = HistoryService::PreparedText { + lang(lng_message_empty) + }; + return HistoryService::create( + history, + data.vid.v, + ::date(data.vdate), + text, + data.vflags.v, + data.has_from_id() ? data.vfrom_id.v : UserId(0)); + } else if (badMedia == MediaCheckResult::HasTimeToLive) { + return HistoryService::create(history, data); + } + return HistoryMessage::create(history, data); + } break; + + case mtpc_messageService: { + auto &data = message.c_messageService(); + if (data.vaction.type() == mtpc_messageActionPhoneCall) { + return HistoryMessage::create(history, data); + } + return HistoryService::create(history, data); + } break; + } + + Unexpected("Type in HistoryItem::Create()."); +} diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 1ed5c15839..cec76382ea 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -14,8 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" enum class UnreadMentionType; -struct MessageGroupId; -struct HistoryMessageGroup; struct HistoryMessageReplyMarkup; class ReplyKeyboard; class HistoryMessage; @@ -42,6 +40,7 @@ struct RippleAnimation; namespace Data { struct MessagePosition; +class Media; } // namespace Data namespace Window { @@ -52,21 +51,12 @@ namespace HistoryView { enum class Context : char; } // namespace HistoryView -namespace internal { - -TextSelection unshiftSelection(TextSelection selection, uint16 byLength); -TextSelection shiftSelection(TextSelection selection, uint16 byLength); -inline TextSelection unshiftSelection(TextSelection selection, const Text &byText) { - return ::internal::unshiftSelection(selection, byText.length()); -} -inline TextSelection shiftSelection(TextSelection selection, const Text &byText) { - return ::internal::shiftSelection(selection, byText.length()); -} - -} // namespace internal - -class HistoryItem : public RuntimeComposer { +class HistoryItem : public RuntimeComposer { public: + static not_null Create( + not_null history, + const MTPMessage &message); + virtual void dependencyItemRemoved(HistoryItem *dependency) { } virtual bool updateDependencyItem() { @@ -145,19 +135,12 @@ public: bool isSilent() const { return _flags & MTPDmessage::Flag::f_silent; } - bool hasOutLayout() const; - virtual int32 viewsCount() const { + virtual int viewsCount() const { return hasViews() ? 1 : -1; } virtual bool needCheck() const; - [[nodiscard]] virtual TextSelection adjustSelection( - TextSelection selection, - TextSelectType type) const { - return selection; - } - virtual bool serviceMsg() const { return false; } @@ -173,17 +156,10 @@ public: virtual void addToUnreadMentions(UnreadMentionType type); virtual void eraseFromUnreadMentions() { } - virtual Storage::SharedMediaTypesMask sharedMediaTypes() const; + virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0; + void indexAsNewItem(); - virtual bool hasBubble() const { - return false; - } - - virtual TextWithEntities selectedText(TextSelection selection) const { - return { qsl("[-]"), EntitiesInText() }; - } - virtual QString notificationHeader() const { return QString(); } @@ -204,29 +180,10 @@ public: return { QString(), EntitiesInText() }; } - virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const { - } - virtual ClickHandlerPtr rightActionLink() const { - return ClickHandlerPtr(); - } - virtual bool displayRightAction() const { - return false; - } - virtual void drawRightAction(Painter &p, int left, int top, int outerWidth) const { - } virtual void setViewsCount(int32 count) { } virtual void setRealId(MsgId newId); - virtual bool displayEditedBadge() const { - return false; - } - virtual QDateTime displayedEditDate() const { - return QDateTime(); - } - virtual void refreshEditedBadge() { - } - void drawInDialog( Painter &p, const QRect &r, @@ -242,8 +199,8 @@ public: bool isPinned() const; bool canPin() const; - bool canForward() const; - bool canEdit(const QDateTime &cur) const; + virtual bool allowsForward() const; + virtual bool allowsEdit(const QDateTime &now) const; bool canDelete() const; bool canDeleteForEveryone(const QDateTime &cur) const; bool suggestBanReport() const; @@ -261,7 +218,7 @@ public: } Data::MessagePosition position() const; - HistoryMedia *getMedia() const { + Data::Media *media() const { return _media.get(); } virtual void setText(const TextWithEntities &textWithEntities) { @@ -270,29 +227,6 @@ public: return false; } - virtual int infoWidth() const { - return 0; - } - virtual int timeLeft() const { - return 0; - } - virtual int timeWidth() const { - return 0; - } - virtual bool pointInTime(int right, int bottom, QPoint point, InfoDisplayType type) const { - return false; - } - - int skipBlockWidth() const { - return st::msgDateSpace + infoWidth() - st::msgDateDelta.x(); - } - int skipBlockHeight() const { - return st::msgDateFont->height - st::msgDateDelta.y(); - } - QString skipBlock() const { - return textcmdSkipBlock(skipBlockWidth(), skipBlockHeight()); - } - virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize return nullptr; } @@ -328,25 +262,12 @@ public: } bool isEmpty() const; - bool isHiddenByGroup() const { - return _flags & MTPDmessage_ClientFlag::f_hidden_by_group; - } MessageGroupId groupId() const; - bool groupIdValidityChanged(); - void validateGroupId() { - // Just ignore the result. - groupIdValidityChanged(); - } - void makeGroupMember(not_null leader); - void makeGroupLeader(std::vector> &&others); - HistoryMessageGroup *getFullGroup(); void clipCallback(Media::Clip::Notification notification); void audioTrackUpdated(); - bool isUnderCursor() const; - HistoryItem *previousItem() const; HistoryItem *nextItem() const; @@ -388,27 +309,20 @@ protected: ReplyKeyboard *inlineReplyKeyboard(); void invalidateChatsListEntry(); - [[nodiscard]] TextSelection skipTextSelection( - TextSelection selection) const { - return internal::unshiftSelection(selection, _text); - } - [[nodiscard]] TextSelection unskipTextSelection( - TextSelection selection) const { - return internal::shiftSelection(selection, _text); - } + void setGroupId(MessageGroupId groupId); Text _text = { int(st::msgMinWidth) }; int _textWidth = -1; int _textHeight = 0; - HistoryMediaPtr _media; + std::unique_ptr _media; private: - void resetGroupMedia(const std::vector> &others); - HistoryView::Element *_mainView = nullptr; friend class HistoryView::Element; + MessageGroupId _groupId = MessageGroupId::None; + }; // make all the constructors in HistoryItem children protected diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index fbfc6decfb..0913bf5d4e 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/media_audio.h" #include "media/player/media_player_instance.h" #include "auth_session.h" +#include "data/data_media_types.h" #include "data/data_session.h" #include "styles/style_widgets.h" #include "styles/style_history.h" @@ -191,7 +192,7 @@ void HistoryMessageReply::updateName() const { : App::peerName(replyToMsg->author()); replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions()); replyToVersion = replyToMsg->author()->nameVersion; - bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; + bool hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false; int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; int32 w = replyToName.maxWidth(); if (replyToVia) { @@ -207,7 +208,7 @@ void HistoryMessageReply::updateName() const { void HistoryMessageReply::resize(int width) const { if (replyToVia) { - bool hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; + bool hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false; int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; replyToVia->resize(width - st::msgReplyBarSkip - previewSkip - replyToName.maxWidth() - st::msgServiceFont->spacew); } @@ -222,7 +223,13 @@ void HistoryMessageReply::itemRemoved( } } -void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, int y, int w, PaintFlags flags) const { +void HistoryMessageReply::paint( + Painter &p, + not_null holder, + int x, + int y, + int w, + PaintFlags flags) const { bool selected = (flags & PaintFlag::Selected), outbg = holder->hasOutLayout(); style::color bar = st::msgImgReplyBarColor; @@ -234,14 +241,14 @@ void HistoryMessageReply::paint(Painter &p, const HistoryItem *holder, int x, in if (w > st::msgReplyBarSkip) { if (replyToMsg) { - auto hasPreview = replyToMsg->getMedia() ? replyToMsg->getMedia()->hasReplyPreview() : false; + auto hasPreview = replyToMsg->media() ? replyToMsg->media()->hasReplyPreview() : false; if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) { hasPreview = false; } auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; if (hasPreview) { - ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview(); + const auto replyPreview = replyToMsg->media()->replyPreview(); if (!replyPreview->isNull()) { auto to = rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x); auto previewWidth = replyPreview->width() / cIntRetinaFactor(); @@ -834,11 +841,14 @@ void HistoryMessageDate::paint(Painter &p, int y, int w) const { HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default; -HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other) : _page(std::move(other._page)) { +HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal( + HistoryMessageLogEntryOriginal &&other) +: page(std::move(other.page)) { } -HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=(HistoryMessageLogEntryOriginal &&other) { - _page = std::move(other._page); +HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=( + HistoryMessageLogEntryOriginal &&other) { + page = std::move(other.page); return *this; } diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 5fc316db39..68e9dce4b0 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -8,38 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "history/history_item.h" -#include "base/value_ordering.h" class HistoryDocument; -class HistoryWebPage; +class WebPageData; -struct MessageGroupId { - using Underlying = uint64; +namespace HistoryView { +class Element; +} // namespace HistoryView - enum Type : Underlying { - None = 0, - } value; - - MessageGroupId(Type value = None) : value(value) { - } - static MessageGroupId FromRaw(Underlying value) { - return static_cast(value); - } - - explicit operator bool() const { - return value != None; - } - Underlying raw() const { - return static_cast(value); - } - - friend inline Type value_ordering_helper(MessageGroupId value) { - return value.value; - } - -}; - -struct HistoryMessageVia : public RuntimeComponent { +struct HistoryMessageVia : public RuntimeComponent { void create(UserId userId); void resize(int32 availw) const; @@ -50,13 +27,13 @@ struct HistoryMessageVia : public RuntimeComponent { ClickHandlerPtr link; }; -struct HistoryMessageViews : public RuntimeComponent { +struct HistoryMessageViews : public RuntimeComponent { QString _viewsText; int _views = 0; int _viewsWidth = 0; }; -struct HistoryMessageSigned : public RuntimeComponent { +struct HistoryMessageSigned : public RuntimeComponent { void refresh(const QString &date); int maxWidth() const; @@ -64,7 +41,7 @@ struct HistoryMessageSigned : public RuntimeComponent { Text signature; }; -struct HistoryMessageEdited : public RuntimeComponent { +struct HistoryMessageEdited : public RuntimeComponent { void refresh(const QString &date, bool displayed); int maxWidth() const; @@ -72,7 +49,7 @@ struct HistoryMessageEdited : public RuntimeComponent { Text text; }; -struct HistoryMessageForwarded : public RuntimeComponent { +struct HistoryMessageForwarded : public RuntimeComponent { void create(const HistoryMessageVia *via) const; QDateTime originalDate; @@ -85,7 +62,7 @@ struct HistoryMessageForwarded : public RuntimeComponent { +struct HistoryMessageReply : public RuntimeComponent { HistoryMessageReply() = default; HistoryMessageReply(const HistoryMessageReply &other) = delete; HistoryMessageReply(HistoryMessageReply &&other) = delete; @@ -125,7 +102,7 @@ struct HistoryMessageReply : public RuntimeComponent { friend inline constexpr auto is_flag_type(PaintFlag) { return true; }; void paint( Painter &p, - const HistoryItem *holder, + not_null holder, int x, int y, int w, @@ -171,7 +148,7 @@ struct HistoryMessageMarkupButton { }; -struct HistoryMessageReplyMarkup : public RuntimeComponent { +struct HistoryMessageReplyMarkup : public RuntimeComponent { using Button = HistoryMessageMarkupButton; HistoryMessageReplyMarkup() = default; @@ -348,7 +325,7 @@ private: // Any HistoryItem can have this Component for // displaying the day mark above the message. -struct HistoryMessageDate : public RuntimeComponent { +struct HistoryMessageDate : public RuntimeComponent { void init(const QDateTime &date); int height() const; @@ -360,7 +337,7 @@ struct HistoryMessageDate : public RuntimeComponent { // Any HistoryItem can have this Component for // displaying the unread messages bar above the message. -struct HistoryMessageUnreadBar : public RuntimeComponent { +struct HistoryMessageUnreadBar : public RuntimeComponent { void init(int count); static int height(); @@ -381,25 +358,20 @@ struct HistoryMessageUnreadBar : public RuntimeComponent { - MessageGroupId groupId = MessageGroupId::None; - HistoryItem *leader = nullptr; - std::vector> others; -}; - // Special type of Component for the channel actions log. -struct HistoryMessageLogEntryOriginal : public RuntimeComponent { +struct HistoryMessageLogEntryOriginal + : public RuntimeComponent { HistoryMessageLogEntryOriginal(); HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other); HistoryMessageLogEntryOriginal &operator=(HistoryMessageLogEntryOriginal &&other); ~HistoryMessageLogEntryOriginal(); - std::unique_ptr _page; + WebPageData *page = nullptr; }; class FileClickHandler; -struct HistoryDocumentThumbed : public RuntimeComponent { +struct HistoryDocumentThumbed : public RuntimeComponent { std::shared_ptr _linksavel, _linkcancell; int _thumbw = 0; @@ -407,13 +379,13 @@ struct HistoryDocumentThumbed : public RuntimeComponent mutable QString _link; }; -struct HistoryDocumentCaptioned : public RuntimeComponent { +struct HistoryDocumentCaptioned : public RuntimeComponent { HistoryDocumentCaptioned(); Text _caption; }; -struct HistoryDocumentNamed : public RuntimeComponent { +struct HistoryDocumentNamed : public RuntimeComponent { QString _name; int _namew = 0; }; @@ -426,7 +398,7 @@ struct HistoryDocumentVoicePlayback { BasicAnimation _a_progress; }; -class HistoryDocumentVoice : public RuntimeComponent { +class HistoryDocumentVoice : public RuntimeComponent { // We don't use float64 because components should align to pointer even on 32bit systems. static constexpr float64 kFloatToIntMultiplier = 65536.; diff --git a/Telegram/SourceFiles/history/history_media.cpp b/Telegram/SourceFiles/history/history_media.cpp index 0feae83f5a..00440f0228 100644 --- a/Telegram/SourceFiles/history/history_media.cpp +++ b/Telegram/SourceFiles/history/history_media.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_media.h" #include "history/history_item.h" +#include "history/view/history_view_element.h" #include "storage/storage_shared_media.h" Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const { @@ -23,11 +24,15 @@ QSize HistoryMedia::countCurrentSize(int newWidth) { } TextSelection HistoryMedia::skipSelection(TextSelection selection) const { - return internal::unshiftSelection(selection, fullSelectionLength()); + return HistoryView::UnshiftItemSelection( + selection, + fullSelectionLength()); } TextSelection HistoryMedia::unskipSelection(TextSelection selection) const { - return internal::shiftSelection(selection, fullSelectionLength()); + return HistoryView::ShiftItemSelection( + selection, + fullSelectionLength()); } HistoryTextState HistoryMedia::getStateGrouped( diff --git a/Telegram/SourceFiles/history/history_media.h b/Telegram/SourceFiles/history/history_media.h index 3bdc4d7c31..738e30c129 100644 --- a/Telegram/SourceFiles/history/history_media.h +++ b/Telegram/SourceFiles/history/history_media.h @@ -52,21 +52,18 @@ enum HistoryMediaType : char { class HistoryMedia : public HistoryView::Object { public: - HistoryMedia(not_null parent) : _parent(parent) { + using Element = HistoryView::Element; + + HistoryMedia(not_null parent) : _parent(parent) { } virtual HistoryMediaType type() const = 0; - - virtual QString notificationText() const { - return QString(); + virtual std::unique_ptr clone( + not_null newParent, + not_null realParent) const { + Unexpected("Attempt to clone a media that can't be grouped."); } - // Returns text with link-start and link-end commands for service-color highlighting. - // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" - virtual QString inDialogsText() const { - auto result = notificationText(); - return result.isEmpty() ? QString() : textcmdLink(1, TextUtilities::Clean(result)); - } virtual TextWithEntities selectedText(TextSelection selection) const = 0; bool hasPoint(QPoint point) const { @@ -85,7 +82,7 @@ public: virtual bool allowsFastShare() const { return false; } - virtual void refreshParentId(not_null realParent) { + virtual void refreshParentId(not_null realParent) { } virtual void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const = 0; virtual HistoryTextState getState(QPoint point, HistoryStateRequest request) const = 0; @@ -109,10 +106,6 @@ public: TextSelectType type) const { return selection; } - [[nodiscard]] virtual bool consumeMessageText( - const TextWithEntities &textWithEntities) { - return false; - } [[nodiscard]] virtual uint16 fullSelectionLength() const { return 0; } @@ -132,9 +125,6 @@ public: virtual bool uploading() const { return false; } - virtual std::unique_ptr clone( - not_null newParent, - not_null realParent) const = 0; virtual PhotoData *getPhoto() const { return nullptr; @@ -187,20 +177,10 @@ public: virtual std::unique_ptr takeLastFromGroup() { return nullptr; } - virtual bool applyGroup( - const std::vector> &others) { + virtual bool applyGroup(const std::vector> &others) { return others.empty(); } - virtual void updateSentMedia(const MTPMessageMedia &media) { - } - - // After sending an inline result we may want to completely recreate - // the media (all media that was generated on client side, for example) - virtual bool needReSetInlineResultMedia(const MTPMessageMedia &media) { - return true; - } - virtual bool animating() const { return false; } @@ -253,10 +233,6 @@ public: return false; } - virtual bool canEditCaption() const { - return false; - } - // Sometimes click on media in message is overloaded by the message: // (for example it can open a link or a game instead of opening media) // But the overloading click handler should be used only when media @@ -271,7 +247,7 @@ public: protected: QSize countCurrentSize(int newWidth) override; - not_null _parent; + not_null _parent; MediaInBubbleState _inBubbleState = MediaInBubbleState::None; }; diff --git a/Telegram/SourceFiles/history/history_media_grouped.cpp b/Telegram/SourceFiles/history/history_media_grouped.cpp index 476b7d3a5d..66b8e3471b 100644 --- a/Telegram/SourceFiles/history/history_media_grouped.cpp +++ b/Telegram/SourceFiles/history/history_media_grouped.cpp @@ -18,13 +18,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_history.h" #include "layout.h" -HistoryGroupedMedia::Element::Element(not_null item) -: item(item) { +HistoryGroupedMedia::Part::Part(not_null view) +: view(view) { } HistoryGroupedMedia::HistoryGroupedMedia( - not_null parent, - const std::vector> &others) + not_null parent, + const std::vector> &others) : HistoryMedia(parent) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { const auto result = applyGroup(others); @@ -32,23 +32,17 @@ HistoryGroupedMedia::HistoryGroupedMedia( Ensures(result); } -std::unique_ptr HistoryGroupedMedia::clone( - not_null newParent, - not_null realParent) const { - return main()->clone(newParent, realParent); -} - QSize HistoryGroupedMedia::countOptimalSize() { if (_caption.hasSkipBlock()) { - _caption.setSkipBlock( + _caption.updateSkipBlock( _parent->skipBlockWidth(), _parent->skipBlockHeight()); } std::vector sizes; - sizes.reserve(_elements.size()); - for (const auto &element : _elements) { - const auto &media = element.content; + sizes.reserve(_parts.size()); + for (const auto &part : _parts) { + const auto &media = part.content; media->initDimensions(); sizes.push_back(media->sizeForGrouping()); } @@ -58,7 +52,7 @@ QSize HistoryGroupedMedia::countOptimalSize() { st::historyGroupWidthMax, st::historyGroupWidthMin, st::historyGroupSkip); - Assert(layout.size() == _elements.size()); + Assert(layout.size() == _parts.size()); auto maxWidth = 0; auto minHeight = 0; @@ -66,8 +60,8 @@ QSize HistoryGroupedMedia::countOptimalSize() { const auto &item = layout[i]; accumulate_max(maxWidth, item.geometry.x() + item.geometry.width()); accumulate_max(minHeight, item.geometry.y() + item.geometry.height()); - _elements[i].initialGeometry = item.geometry; - _elements[i].sides = item.sides; + _parts[i].initialGeometry = item.geometry; + _parts[i].sides = item.sides; } if (!_caption.isEmpty()) { @@ -93,9 +87,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) { return int(std::round(value * factor)); }; const auto spacing = scale(initialSpacing); - for (auto &element : _elements) { - const auto sides = element.sides; - const auto initialGeometry = element.initialGeometry; + for (auto &part : _parts) { + const auto sides = part.sides; + const auto initialGeometry = part.initialGeometry; const auto needRightSkip = !(sides & RectPart::Right); const auto needBottomSkip = !(sides & RectPart::Bottom); const auto initialLeft = initialGeometry.x(); @@ -114,7 +108,7 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) { const auto height = scale(initialBottom) - top - (needBottomSkip ? spacing : 0); - element.geometry = QRect(left, top, width, height); + part.geometry = QRect(left, top, width, height); accumulate_max(newHeight, top + height); } @@ -130,10 +124,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -void HistoryGroupedMedia::refreshParentId( - not_null realParent) { - for (const auto &element : _elements) { - element.content->refreshParentId(element.item); +void HistoryGroupedMedia::refreshParentId(not_null realParent) { + for (const auto &part : _parts) { + part.content->refreshParentId(part.view); } } @@ -142,29 +135,29 @@ void HistoryGroupedMedia::draw( const QRect &clip, TextSelection selection, TimeMs ms) const { - for (auto i = 0, count = int(_elements.size()); i != count; ++i) { - const auto &element = _elements[i]; - const auto elementSelection = (selection == FullSelection) + for (auto i = 0, count = int(_parts.size()); i != count; ++i) { + const auto &part = _parts[i]; + const auto partSelection = (selection == FullSelection) ? FullSelection : IsGroupItemSelection(selection, i) ? FullSelection : TextSelection(); - auto corners = Ui::GetCornersFromSides(element.sides); + auto corners = Ui::GetCornersFromSides(part.sides); if (!isBubbleTop()) { corners &= ~(RectPart::TopLeft | RectPart::TopRight); } if (!isBubbleBottom() || !_caption.isEmpty()) { corners &= ~(RectPart::BottomLeft | RectPart::BottomRight); } - element.content->drawGrouped( + part.content->drawGrouped( p, clip, - elementSelection, + partSelection, ms, - element.geometry, + part.geometry, corners, - &element.cacheKey, - &element.cache); + &part.cacheKey, + &part.cache); } // date @@ -177,7 +170,7 @@ void HistoryGroupedMedia::draw( - _caption.countHeight(captionw); p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); _caption.draw(p, st::msgPadding.left(), captiony, captionw, style::al_left, 0, -1, selection); - } else if (_parent->getMedia() == this) { + } else if (_parent->media() == this) { auto fullRight = width(); auto fullBottom = height(); if (needInfoDisplay()) { @@ -191,38 +184,38 @@ void HistoryGroupedMedia::draw( } } -HistoryTextState HistoryGroupedMedia::getElementState( +HistoryTextState HistoryGroupedMedia::getPartState( QPoint point, HistoryStateRequest request) const { - for (const auto &element : _elements) { - if (element.geometry.contains(point)) { - auto result = element.content->getStateGrouped( - element.geometry, + for (const auto &part : _parts) { + if (part.geometry.contains(point)) { + auto result = part.content->getStateGrouped( + part.geometry, point, request); - result.itemId = element.item->fullId(); + result.itemId = part.view->data()->fullId(); return result; } } - return HistoryTextState(_parent); + return HistoryTextState(_parent->data()); } HistoryTextState HistoryGroupedMedia::getState( QPoint point, HistoryStateRequest request) const { - auto result = getElementState(point, request); + auto result = getPartState(point, request); if (!result.link && !_caption.isEmpty()) { const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right(); const auto captiony = height() - (isBubbleBottom() ? st::msgPadding.bottom() : 0) - _caption.countHeight(captionw); if (QRect(st::msgPadding.left(), captiony, captionw, height() - captiony).contains(point)) { - return HistoryTextState(_parent, _caption.getState( + return HistoryTextState(_parent->data(), _caption.getState( point - QPoint(st::msgPadding.left(), captiony), captionw, request.forText())); } - } else if (_parent->getMedia() == this) { + } else if (_parent->media() == this) { auto fullRight = width(); auto fullBottom = height(); if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) { @@ -241,8 +234,8 @@ HistoryTextState HistoryGroupedMedia::getState( bool HistoryGroupedMedia::toggleSelectionByHandlerClick( const ClickHandlerPtr &p) const { - for (const auto &element : _elements) { - if (element.content->toggleSelectionByHandlerClick(p)) { + for (const auto &part : _parts) { + if (part.content->toggleSelectionByHandlerClick(p)) { return true; } } @@ -250,8 +243,8 @@ bool HistoryGroupedMedia::toggleSelectionByHandlerClick( } bool HistoryGroupedMedia::dragItemByHandler(const ClickHandlerPtr &p) const { - for (const auto &element : _elements) { - if (element.content->dragItemByHandler(p)) { + for (const auto &part : _parts) { + if (part.content->dragItemByHandler(p)) { return true; } } @@ -264,14 +257,6 @@ TextSelection HistoryGroupedMedia::adjustSelection( return _caption.adjustSelection(selection, type); } -QString HistoryGroupedMedia::notificationText() const { - return WithCaptionNotificationText(lang(lng_in_dlg_photo), _caption); -} - -QString HistoryGroupedMedia::inDialogsText() const { - return WithCaptionDialogsText(lang(lng_in_dlg_album), _caption); -} - TextWithEntities HistoryGroupedMedia::selectedText( TextSelection selection) const { if (!IsSubGroupSelection(selection)) { @@ -279,7 +264,7 @@ TextWithEntities HistoryGroupedMedia::selectedText( lang(lng_in_dlg_album), _caption, selection); - } else if (IsGroupItemSelection(selection, int(_elements.size()) - 1)) { + } else if (IsGroupItemSelection(selection, int(_parts.size()) - 1)) { return main()->selectedText(FullSelection); } return TextWithEntities(); @@ -288,78 +273,77 @@ TextWithEntities HistoryGroupedMedia::selectedText( void HistoryGroupedMedia::clickHandlerActiveChanged( const ClickHandlerPtr &p, bool active) { - for (const auto &element : _elements) { - element.content->clickHandlerActiveChanged(p, active); + for (const auto &part : _parts) { + part.content->clickHandlerActiveChanged(p, active); } } void HistoryGroupedMedia::clickHandlerPressedChanged( const ClickHandlerPtr &p, bool pressed) { - for (const auto &element : _elements) { - element.content->clickHandlerPressedChanged(p, pressed); - if (pressed && element.content->dragItemByHandler(p)) { - // #TODO pressedLinkItem - //App::pressedLinkItem(element.item); + for (const auto &part : _parts) { + part.content->clickHandlerPressedChanged(p, pressed); + if (pressed && part.content->dragItemByHandler(p)) { + App::pressedLinkItem(part.view); } } } void HistoryGroupedMedia::attachToParent() { - for (const auto &element : _elements) { - element.content->attachToParent(); + for (const auto &part : _parts) { + part.content->attachToParent(); } } void HistoryGroupedMedia::detachFromParent() { - for (const auto &element : _elements) { - if (element.content) { - element.content->detachFromParent(); + for (const auto &part : _parts) { + if (part.content) { + part.content->detachFromParent(); } } } std::unique_ptr HistoryGroupedMedia::takeLastFromGroup() { - return std::move(_elements.back().content); + return std::move(_parts.back().content); } bool HistoryGroupedMedia::applyGroup( - const std::vector> &others) { + const std::vector> &others) { if (others.empty()) { return false; } - const auto pushElement = [&](not_null item) { - const auto media = item->getMedia(); + const auto pushElement = [&](not_null view) { + const auto media = view->media(); Assert(media != nullptr && media->canBeGrouped()); - _elements.push_back(Element(item)); - _elements.back().content = item->getMedia()->clone(_parent, item); + _parts.push_back(Part(view)); + _parts.back().content = media->clone(_parent, view); }; - if (_elements.empty()) { + if (_parts.empty()) { pushElement(_parent); - } else if (validateGroupElements(others)) { + } else if (validateGroupParts(others)) { return true; } // We're updating other elements, so we just need to preserve the main. - auto mainElement = std::move(_elements.back()); - _elements.erase(_elements.begin(), _elements.end()); - _elements.reserve(others.size() + 1); - for (const auto item : others) { - pushElement(item); + auto mainPart = std::move(_parts.back()); + _parts.erase(_parts.begin(), _parts.end()); + _parts.reserve(others.size() + 1); + for (const auto view : others) { + pushElement(view); } - _elements.push_back(std::move(mainElement)); + _parts.push_back(std::move(mainPart)); //_parent->setPendingInitDimensions(); // #TODO group view return true; } -bool HistoryGroupedMedia::validateGroupElements( - const std::vector> &others) const { - if (_elements.size() != others.size() + 1) { +bool HistoryGroupedMedia::validateGroupParts( + const std::vector> &others) const { + if (_parts.size() != others.size() + 1) { return false; } for (auto i = 0, count = int(others.size()); i != count; ++i) { - if (_elements[i].item != others[i]) { + if (_parts[i].view != others[i]) { return false; } } @@ -367,9 +351,9 @@ bool HistoryGroupedMedia::validateGroupElements( } not_null HistoryGroupedMedia::main() const { - Expects(!_elements.empty()); + Expects(!_parts.empty()); - return _elements.back().content.get(); + return _parts.back().content.get(); } bool HistoryGroupedMedia::hasReplyPreview() const { @@ -388,15 +372,6 @@ Storage::SharedMediaTypesMask HistoryGroupedMedia::sharedMediaTypes() const { return main()->sharedMediaTypes(); } -void HistoryGroupedMedia::updateSentMedia(const MTPMessageMedia &media) { - return main()->updateSentMedia(media); -} - -bool HistoryGroupedMedia::needReSetInlineResultMedia( - const MTPMessageMedia &media) { - return main()->needReSetInlineResultMedia(media); -} - PhotoData *HistoryGroupedMedia::getPhoto() const { return main()->getPhoto(); } @@ -407,25 +382,25 @@ DocumentData *HistoryGroupedMedia::getDocument() const { HistoryMessageEdited *HistoryGroupedMedia::displayedEditBadge() const { if (!_caption.isEmpty()) { - return _elements.front().item->Get(); + return _parts.front().view->data()->Get(); } return nullptr; } void HistoryGroupedMedia::updateNeedBubbleState() { - const auto getItemCaption = [](const Element &element) { - if (const auto media = element.item->getMedia()) { + const auto getItemCaption = [](const Part &part) { + if (const auto media = part.view->media()) { return media->getCaption(); } - return element.content->getCaption(); + return part.content->getCaption(); }; const auto captionText = [&] { - auto result = getItemCaption(_elements.front()); + auto result = getItemCaption(_parts.front()); if (result.text.isEmpty()) { return result; } - for (auto i = 1, count = int(_elements.size()); i != count; ++i) { - if (!getItemCaption(_elements[i]).text.isEmpty()) { + for (auto i = 1, count = int(_parts.size()); i != count; ++i) { + if (!getItemCaption(_parts[i]).text.isEmpty()) { return TextWithEntities(); } } @@ -434,7 +409,7 @@ void HistoryGroupedMedia::updateNeedBubbleState() { _caption.setText( st::messageTextStyle, captionText.text + _parent->skipBlock(), - Ui::ItemTextNoMonoOptions(_parent)); + Ui::ItemTextNoMonoOptions(_parent->data())); _needBubble = computeNeedBubble(); } @@ -442,19 +417,15 @@ bool HistoryGroupedMedia::needsBubble() const { return _needBubble; } -bool HistoryGroupedMedia::canEditCaption() const { - return main()->canEditCaption(); -} - bool HistoryGroupedMedia::computeNeedBubble() const { if (!_caption.isEmpty()) { return true; } - if (const auto message = _parent->toHistoryMessage()) { - if (message->viaBot() - || message->Has() - || message->displayForwardedFrom() -// || message->displayFromName() // #TODO media views + if (const auto item = _parent->data()) { + if (item->viaBot() + || item->Has() + || _parent->displayForwardedFrom() + || _parent->displayFromName() ) { return true; } @@ -463,5 +434,5 @@ bool HistoryGroupedMedia::computeNeedBubble() const { } bool HistoryGroupedMedia::needInfoDisplay() const { - return (_parent->id < 0 || _parent->isUnderCursor()); + return (_parent->data()->id < 0 || _parent->isUnderCursor()); } diff --git a/Telegram/SourceFiles/history/history_media_grouped.h b/Telegram/SourceFiles/history/history_media_grouped.h index d6ee6b8145..ef02d87bef 100644 --- a/Telegram/SourceFiles/history/history_media_grouped.h +++ b/Telegram/SourceFiles/history/history_media_grouped.h @@ -14,19 +14,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class HistoryGroupedMedia : public HistoryMedia { public: HistoryGroupedMedia( - not_null parent, - const std::vector> &others); + not_null parent, + const std::vector> &others); HistoryMediaType type() const override { return MediaTypeGrouped; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override; - void refreshParentId(not_null realParent) override; - void updateSentMedia(const MTPMessageMedia &media) override; - bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; + void refreshParentId(not_null realParent) override; void draw( Painter &p, @@ -54,8 +49,6 @@ public: PhotoData *getPhoto() const override; DocumentData *getDocument() const override; - QString notificationText() const override; - QString inDialogsText() const override; TextWithEntities selectedText(TextSelection selection) const override; void clickHandlerActiveChanged( @@ -69,7 +62,7 @@ public: void detachFromParent() override; std::unique_ptr takeLastFromGroup() override; bool applyGroup( - const std::vector> &others) override; + const std::vector> &others) override; bool hasReplyPreview() const override; ImagePtr replyPreview() override; @@ -93,16 +86,15 @@ public: bool customInfoLayout() const override { return _caption.isEmpty(); } - bool canEditCaption() const override; bool allowsFastShare() const override { return true; } private: - struct Element { - Element(not_null item); + struct Part { + Part(not_null view); - not_null item; + not_null view; std::unique_ptr content; RectParts sides = RectPart::None; @@ -119,14 +111,14 @@ private: bool needInfoDisplay() const; bool computeNeedBubble() const; not_null main() const; - bool validateGroupElements( - const std::vector> &others) const; - HistoryTextState getElementState( + bool validateGroupParts( + const std::vector> &others) const; + HistoryTextState getPartState( QPoint point, HistoryStateRequest request) const; Text _caption; - std::vector _elements; + std::vector _parts; bool _needBubble = false; }; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index a81b65d639..6012b37f7b 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "history/history_location_manager.h" #include "history/history_message.h" +#include "history/view/history_view_element.h" #include "window/main_window.h" #include "window/window_controller.h" #include "styles/style_history.h" @@ -32,30 +33,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/grouped_layout.h" #include "ui/text_options.h" #include "data/data_session.h" +#include "data/data_media_types.h" namespace { constexpr auto kMaxGifForwardedBarLines = 4; constexpr auto kMaxOriginalEntryLines = 8192; -bool needReSetInlineResultDocument(const MTPMessageMedia &media, DocumentData *existing) { - if (media.type() == mtpc_messageMediaDocument) { - auto &mediaDocument = media.c_messageMediaDocument(); - if (mediaDocument.has_document() && !mediaDocument.has_ttl_seconds()) { - if (auto document = App::feedDocument(mediaDocument.vdocument)) { - if (document == existing) { - return false; - } else { - document->collectLocalData(existing); - } - } - } else { - LOG(("API Error: Got MTPMessageMediaDocument without document or with ttl_seconds in needReSetInlineResultDocument()")); - } - } - return true; -} - int documentMaxStatusWidth(DocumentData *document) { auto result = st::normalFont->width(formatDownloadText(document->size, document->size)); if (const auto song = document->song()) { @@ -101,30 +85,6 @@ TextWithEntities WithCaptionSelectedText( return result; } -QString WithCaptionNotificationText( - const QString &attachType, - const Text &caption) { - if (caption.isEmpty()) { - return attachType; - } - - auto captionText = caption.originalText(); - auto attachTypeWrapped = lng_dialogs_text_media_wrapped(lt_media, attachType); - return lng_dialogs_text_media(lt_media_part, attachTypeWrapped, lt_caption, captionText); -} - -QString WithCaptionDialogsText( - const QString &attachType, - const Text &caption) { - if (caption.isEmpty()) { - return textcmdLink(1, TextUtilities::Clean(attachType)); - } - - auto captionText = TextUtilities::Clean(caption.originalText()); - auto attachTypeWrapped = textcmdLink(1, lng_dialogs_text_media_wrapped(lt_media, TextUtilities::Clean(attachType))); - return lng_dialogs_text_media(lt_media_part, attachTypeWrapped, lt_caption, captionText); -} - void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { if (p == _savel || p == _cancell) { if (active && !dataLoaded()) { @@ -137,13 +97,13 @@ void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool } void HistoryFileMedia::thumbAnimationCallback() { - Auth().data().requestItemRepaint(_parent); + Auth().data().requestViewRepaint(_parent); } void HistoryFileMedia::clickHandlerPressedChanged( const ClickHandlerPtr &handler, bool pressed) { - Auth().data().requestItemRepaint(_parent); + Auth().data().requestViewRepaint(_parent); } void HistoryFileMedia::setLinks( @@ -155,8 +115,8 @@ void HistoryFileMedia::setLinks( _cancell = std::move(cancell); } -void HistoryFileMedia::refreshParentId(not_null realParent) { - const auto contextId = realParent->fullId(); +void HistoryFileMedia::refreshParentId(not_null realParent) { + const auto contextId = realParent->data()->fullId(); _openl->setMessageId(contextId); _savel->setMessageId(contextId); _cancell->setMessageId(contextId); @@ -179,7 +139,7 @@ void HistoryFileMedia::setStatusSize(int newSize, int fullSize, int duration, qi void HistoryFileMedia::step_radial(TimeMs ms, bool timer) { if (timer) { - Auth().data().requestItemRepaint(_parent); + Auth().data().requestViewRepaint(_parent); } else { _animation->radial.update(dataProgress(), dataFinished(), ms); if (!_animation->radial.animating()) { @@ -228,13 +188,14 @@ void HistoryFileMedia::setDocumentLinks( HistoryFileMedia::~HistoryFileMedia() = default; HistoryPhoto::HistoryPhoto( - not_null parent, + not_null parent, not_null photo, const QString &caption) : HistoryFileMedia(parent) , _data(photo) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { - const auto fullId = parent->fullId(); + const auto item = parent->data(); + const auto fullId = item->fullId(); setLinks( std::make_shared(_data, fullId), std::make_shared(_data, fullId), @@ -243,20 +204,20 @@ HistoryPhoto::HistoryPhoto( _caption.setText( st::messageTextStyle, caption + _parent->skipBlock(), - Ui::ItemTextNoMonoOptions(_parent)); + Ui::ItemTextNoMonoOptions(item)); } init(); } HistoryPhoto::HistoryPhoto( - not_null parent, + not_null parent, not_null chat, not_null photo, int width) : HistoryFileMedia(parent) , _data(photo) , _serviceWidth(width) { - const auto fullId = parent->fullId(); + const auto fullId = parent->data()->fullId(); setLinks( std::make_shared(_data, fullId, chat), std::make_shared(_data, fullId, chat), @@ -265,7 +226,7 @@ HistoryPhoto::HistoryPhoto( } HistoryPhoto::HistoryPhoto( - not_null parent, + not_null parent, not_null chat, const MTPDphoto &photo, int width) @@ -273,15 +234,15 @@ HistoryPhoto::HistoryPhoto( } HistoryPhoto::HistoryPhoto( - not_null parent, - not_null realParent, + not_null parent, + not_null realParent, const HistoryPhoto &other) : HistoryFileMedia(parent) , _data(other._data) , _pixw(other._pixw) , _pixh(other._pixh) , _caption(other._caption) { - const auto fullId = realParent->fullId(); + const auto fullId = realParent->data()->fullId(); setLinks( std::make_shared(_data, fullId), std::make_shared(_data, fullId), @@ -296,7 +257,9 @@ void HistoryPhoto::init() { QSize HistoryPhoto::countOptimalSize() { if (_caption.hasSkipBlock()) { - _caption.setSkipBlock(_parent->skipBlockWidth(), _parent->skipBlockHeight()); + _caption.updateSkipBlock( + _parent->skipBlockWidth(), + _parent->skipBlockHeight()); } auto maxWidth = 0; @@ -316,7 +279,7 @@ QSize HistoryPhoto::countOptimalSize() { th = st::maxMediaSize; } - if (!_parent->toHistoryMessage()) { + if (_serviceWidth > 0) { return { _serviceWidth, _serviceWidth }; } const auto minWidth = qMax(st::minPhotoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); @@ -376,12 +339,12 @@ QSize HistoryPhoto::countCurrentSize(int newWidth) { void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - _data->automaticLoad(_parent); + _data->automaticLoad(_parent->data()); auto selected = (selection == FullSelection); auto loaded = _data->loaded(); auto displayLoading = _data->displayLoading(); - auto notChild = (_parent->getMedia() == this); + auto inWebPage = (_parent->media() != this); auto paintx = 0, painty = 0, paintw = width(), painth = height(); auto bubble = _parent->hasBubble(); @@ -396,7 +359,12 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim bool radial = isRadialAnimation(ms); auto rthumb = rtlrect(paintx, painty, paintw, painth, width()); - if (_parent->toHistoryMessage()) { + if (_serviceWidth > 0) { + const auto pix = loaded + ? _data->full->pixCircled(_pixw, _pixh) + : _data->thumb->pixBlurredCircled(_pixw, _pixh); + p.drawPixmap(rthumb.topLeft(), pix); + } else { if (bubble) { if (!_caption.isEmpty()) { painth -= st::mediaCaptionSkip + _caption.countHeight(captionw); @@ -408,7 +376,7 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim } else { App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } - auto inWebPage = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); @@ -419,11 +387,6 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim if (selected) { App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); } - } else { - const auto pix = loaded - ? _data->full->pixCircled(_pixw, _pixh) - : _data->thumb->pixBlurredCircled(_pixw, _pixh); - p.drawPixmap(rthumb.topLeft(), pix); } if (radial || (!loaded && !_data->loading())) { const auto radialOpacity = (radial && loaded && !_data->uploading()) @@ -474,7 +437,7 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim auto outbg = _parent->hasOutLayout(); p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); - } else if (notChild) { + } else if (!inWebPage) { auto fullRight = paintx + paintw; auto fullBottom = painty + painth; if (needInfoDisplay()) { @@ -528,7 +491,7 @@ HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest reques result.link = _savel; } } - if (_caption.isEmpty() && _parent->getMedia() == this) { + if (_caption.isEmpty() && _parent->media() == this) { auto fullRight = paintx + paintw; auto fullBottom = painty + painth; if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) { @@ -560,7 +523,7 @@ void HistoryPhoto::drawGrouped( RectParts corners, not_null cacheKey, not_null cache) const { - _data->automaticLoad(_parent); + _data->automaticLoad(_parent->data()); validateGroupedCache(geometry, corners, cacheKey, cache); @@ -719,93 +682,6 @@ void HistoryPhoto::validateGroupedCache( *cache = image->pixNoCache(pixWidth, pixHeight, options, width, height); } -void HistoryPhoto::updateSentMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaPhoto) { - auto &mediaPhoto = media.c_messageMediaPhoto(); - if (!mediaPhoto.has_photo() || mediaPhoto.has_ttl_seconds()) { - LOG(("Api Error: Got MTPMessageMediaPhoto without photo or with ttl_seconds in updateSentMedia()")); - return; - } - auto &photo = mediaPhoto.vphoto; - App::feedPhoto(photo, _data); - - if (photo.type() == mtpc_photo) { - auto &sizes = photo.c_photo().vsizes.v; - auto max = 0; - const MTPDfileLocation *maxLocation = 0; - for (auto i = 0, l = int(sizes.size()); i != l; ++i) { - char size = 0; - const MTPFileLocation *loc = 0; - switch (sizes.at(i).type()) { - case mtpc_photoSize: { - auto &s = sizes.at(i).c_photoSize().vtype.v; - loc = &sizes.at(i).c_photoSize().vlocation; - if (s.size()) size = s[0]; - } break; - - case mtpc_photoCachedSize: { - auto &s = sizes.at(i).c_photoCachedSize().vtype.v; - loc = &sizes.at(i).c_photoCachedSize().vlocation; - if (s.size()) size = s[0]; - } break; - } - if (!loc || loc->type() != mtpc_fileLocation) continue; - if (size == 's') { - Local::writeImage(storageKey(loc->c_fileLocation()), _data->thumb); - } else if (size == 'm') { - Local::writeImage(storageKey(loc->c_fileLocation()), _data->medium); - } else if (size == 'x' && max < 1) { - max = 1; - maxLocation = &loc->c_fileLocation(); - } else if (size == 'y' && max < 2) { - max = 2; - maxLocation = &loc->c_fileLocation(); - //} else if (size == 'w' && max < 3) { - // max = 3; - // maxLocation = &loc->c_fileLocation(); - } - } - if (maxLocation) { - Local::writeImage(storageKey(*maxLocation), _data->full); - } - } - } -} - -bool HistoryPhoto::needReSetInlineResultMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaPhoto) { - auto &photo = media.c_messageMediaPhoto(); - if (photo.has_photo() && !photo.has_ttl_seconds()) { - if (auto existing = App::feedPhoto(photo.vphoto)) { - if (existing == _data) { - return false; - } else { - // collect data - } - } - } else { - LOG(("API Error: Got MTPMessageMediaPhoto without photo or with ttl_seconds in needReSetInlineResultMedia()")); - } - } - return false; -} - -void HistoryPhoto::attachToParent() { - App::regPhotoItem(_data, _parent); -} - -void HistoryPhoto::detachFromParent() { - App::unregPhotoItem(_data, _parent); -} - -QString HistoryPhoto::notificationText() const { - return WithCaptionNotificationText(lang(lng_in_dlg_photo), _caption); -} - -QString HistoryPhoto::inDialogsText() const { - return WithCaptionDialogsText(lang(lng_in_dlg_photo), _caption); -} - TextWithEntities HistoryPhoto::selectedText(TextSelection selection) const { return WithCaptionSelectedText( lang(lng_in_dlg_photo), @@ -817,46 +693,37 @@ bool HistoryPhoto::needsBubble() const { if (!_caption.isEmpty()) { return true; } - if (auto message = _parent->toHistoryMessage()) { - return message->viaBot() - || message->Has() - || message->displayForwardedFrom() -// || message->displayFromName() // #TODO media views -; + const auto item = _parent->data(); + if (item->toHistoryMessage()) { + return item->viaBot() + || item->Has() + || _parent->displayForwardedFrom() + || _parent->displayFromName(); } return false; } -Storage::SharedMediaTypesMask HistoryPhoto::sharedMediaTypes() const { - using Type = Storage::SharedMediaType; - if (_parent->toHistoryMessage()) { - return Storage::SharedMediaTypesMask{} - .added(Type::Photo) - .added(Type::PhotoVideo); - } - return Type::ChatPhoto; -} - ImagePtr HistoryPhoto::replyPreview() { return _data->makeReplyPreview(); } HistoryVideo::HistoryVideo( - not_null parent, + not_null parent, not_null document, const QString &caption) : HistoryFileMedia(parent) , _data(document) , _thumbw(1) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { + const auto item = parent->data(); if (!caption.isEmpty()) { _caption.setText( st::messageTextStyle, caption + _parent->skipBlock(), - Ui::ItemTextNoMonoOptions(_parent)); + Ui::ItemTextNoMonoOptions(item)); } - setDocumentLinks(_data, parent); + setDocumentLinks(_data, item); setStatusSize(FileStatusSizeReady); @@ -864,21 +731,23 @@ HistoryVideo::HistoryVideo( } HistoryVideo::HistoryVideo( - not_null parent, - not_null realParent, + not_null parent, + not_null realParent, const HistoryVideo &other) : HistoryFileMedia(parent) , _data(other._data) , _thumbw(other._thumbw) , _caption(other._caption) { - setDocumentLinks(_data, realParent); + setDocumentLinks(_data, realParent->data()); setStatusSize(other._statusSize); } QSize HistoryVideo::countOptimalSize() { if (_caption.hasSkipBlock()) { - _caption.setSkipBlock(_parent->skipBlockWidth(), _parent->skipBlockHeight()); + _caption.updateSkipBlock( + _parent->skipBlockWidth(), + _parent->skipBlockHeight()); } auto tw = convertScale(_data->thumb->width()); @@ -949,7 +818,7 @@ QSize HistoryVideo::countCurrentSize(int newWidth) { void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - _data->automaticLoad(_parent); + _data->automaticLoad(_parent->data()); bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); bool selected = (selection == FullSelection); @@ -978,7 +847,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } - auto inWebPage = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); @@ -1013,7 +882,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim if (loaded && !radial) { return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); } else if (radial || _data->loading()) { - if (_parent->id > 0 || _data->uploading()) { + if (_parent->data()->id > 0 || _data->uploading()) { return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); } return nullptr; @@ -1041,7 +910,7 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim auto outbg = _parent->hasOutLayout(); p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); - } else if (_parent->getMedia() == this) { + } else if (_parent->media() == this) { auto fullRight = paintx + paintw, fullBottom = painty + painth; _parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayOverImage); if (!bubble && _parent->displayRightAction()) { @@ -1086,7 +955,7 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques result.link = loaded ? _openl : (_data->loading() ? _cancell : _savel); } } - if (_caption.isEmpty() && _parent->getMedia() == this) { + if (_caption.isEmpty() && _parent->media() == this) { auto fullRight = paintx + paintw; auto fullBottom = painty + painth; if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) { @@ -1118,7 +987,7 @@ void HistoryVideo::drawGrouped( RectParts corners, not_null cacheKey, not_null cache) const { - _data->automaticLoad(_parent); + _data->automaticLoad(_parent->data()); validateGroupedCache(geometry, corners, cacheKey, cache); @@ -1178,7 +1047,7 @@ void HistoryVideo::drawGrouped( } else if (loaded && !radial) { return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); } else if (radial || _data->loading()) { - if (_parent->id > 0 || _data->uploading()) { + if (_parent->data()->id > 0 || _data->uploading()) { return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); } return nullptr; @@ -1270,14 +1139,6 @@ void HistoryVideo::setStatusSize(int newSize) const { HistoryFileMedia::setStatusSize(newSize, _data->size, _data->duration(), 0); } -QString HistoryVideo::notificationText() const { - return WithCaptionNotificationText(lang(lng_in_dlg_video), _caption); -} - -QString HistoryVideo::inDialogsText() const { - return WithCaptionDialogsText(lang(lng_in_dlg_video), _caption); -} - TextWithEntities HistoryVideo::selectedText(TextSelection selection) const { return WithCaptionSelectedText( lang(lng_in_dlg_video), @@ -1289,23 +1150,14 @@ bool HistoryVideo::needsBubble() const { if (!_caption.isEmpty()) { return true; } - if (auto message = _parent->toHistoryMessage()) { - return message->viaBot() - || message->Has() - || message->displayForwardedFrom() -// || message->displayFromName() // #TODO media views -; - } + const auto item = _parent->data(); + return item->viaBot() + || item->Has() + || _parent->displayForwardedFrom() + || _parent->displayFromName(); return false; } -Storage::SharedMediaTypesMask HistoryVideo::sharedMediaTypes() const { - using Type = Storage::SharedMediaType; - return Storage::SharedMediaTypesMask{} - .added(Type::Video) - .added(Type::PhotoVideo); -} - void HistoryVideo::updateStatusText() const { auto showPause = false; auto statusSize = 0; @@ -1326,29 +1178,6 @@ void HistoryVideo::updateStatusText() const { } } -void HistoryVideo::attachToParent() { - App::regDocumentItem(_data, _parent); -} - -void HistoryVideo::detachFromParent() { - App::unregDocumentItem(_data, _parent); -} - -void HistoryVideo::updateSentMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaDocument) { - auto &mediaDocument = media.c_messageMediaDocument(); - if (!mediaDocument.has_document() || mediaDocument.has_ttl_seconds()) { - LOG(("Api Error: Got MTPMessageMediaDocument without document or with ttl_seconds in HistoryVideo::updateSentMedia()")); - return; - } - App::feedDocument(mediaDocument.vdocument, _data); - } -} - -bool HistoryVideo::needReSetInlineResultMedia(const MTPMessageMedia &media) { - return needReSetInlineResultDocument(media, _data); -} - ImagePtr HistoryVideo::replyPreview() { if (_data->replyPreview->isNull() && !_data->thumb->isNull()) { if (_data->thumb->loaded()) { @@ -1365,17 +1194,19 @@ ImagePtr HistoryVideo::replyPreview() { } HistoryDocument::HistoryDocument( - not_null parent, + not_null parent, not_null document, const QString &caption) : HistoryFileMedia(parent) , _data(document) { + const auto item = parent->data(); + createComponents(!caption.isEmpty()); if (auto named = Get()) { fillNamedFromData(named); } - setDocumentLinks(_data, parent); + setDocumentLinks(_data, item); setStatusSize(FileStatusSizeReady); @@ -1383,32 +1214,7 @@ HistoryDocument::HistoryDocument( captioned->_caption.setText( st::messageTextStyle, caption + _parent->skipBlock(), - Ui::ItemTextNoMonoOptions(_parent)); - } -} - -HistoryDocument::HistoryDocument( - not_null parent, - const HistoryDocument &other) -: HistoryFileMedia(parent) -, _data(other._data) { - auto captioned = other.Get(); - createComponents(captioned != 0); - if (auto named = Get()) { - if (auto othernamed = other.Get()) { - named->_name = othernamed->_name; - named->_namew = othernamed->_namew; - } else { - fillNamedFromData(named); - } - } - - setDocumentLinks(_data, parent); - - setStatusSize(other._statusSize); - - if (captioned) { - Get()->_caption = captioned->_caption; + Ui::ItemTextNoMonoOptions(item)); } } @@ -1445,15 +1251,15 @@ void HistoryDocument::createComponents(bool caption) { if (auto thumbed = Get()) { thumbed->_linksavel = std::make_shared( _data, - _parent->fullId()); + _parent->data()->fullId()); thumbed->_linkcancell = std::make_shared( _data, - _parent->fullId()); + _parent->data()->fullId()); } if (auto voice = Get()) { voice->_seekl = std::make_shared( _data, - _parent->fullId()); + _parent->data()->fullId()); } } @@ -1463,9 +1269,13 @@ void HistoryDocument::fillNamedFromData(HistoryDocumentNamed *named) { } QSize HistoryDocument::countOptimalSize() { + const auto item = _parent->data(); + auto captioned = Get(); if (captioned && captioned->_caption.hasSkipBlock()) { - captioned->_caption.setSkipBlock(_parent->skipBlockWidth(), _parent->skipBlockHeight()); + captioned->_caption.updateSkipBlock( + _parent->skipBlockWidth(), + _parent->skipBlockHeight()); } auto thumbed = Get(); @@ -1506,7 +1316,7 @@ QSize HistoryDocument::countOptimalSize() { } else { minHeight = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom(); } - if (!captioned && (_parent->Has() || _parent->displayEditedBadge())) { + if (!captioned && (item->Has() || _parent->displayEditedBadge())) { minHeight += st::msgDateFont->height - st::msgDateDelta.y(); } if (!isBubbleTop()) { @@ -1553,7 +1363,7 @@ QSize HistoryDocument::countCurrentSize(int newWidth) { void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - _data->automaticLoad(_parent); + _data->automaticLoad(_parent->data()); bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); bool selected = (selection == FullSelection); @@ -1579,7 +1389,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, linktop = st::msgFileThumbLinkTop - topMinus; bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom() - topMinus; - auto inWebPage = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, width())); QPixmap thumb; @@ -1725,7 +1535,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, auto wf_size = wf ? wf->size() : Media::Player::kWaveformSamplesCount; auto availw = namewidth + st::msgWaveformSkip; auto activew = qRound(availw * progress); - if (!outbg && !voice->_playback && _parent->isMediaUnread()) { + if (!outbg && !voice->_playback && _parent->data()->isMediaUnread()) { activew = availw; } auto bar_count = qMin(availw / (st::msgWaveformBar + st::msgWaveformSkip), wf_size); @@ -1779,7 +1589,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, p.setPen(status); p.drawTextLeft(nameleft, statustop, width(), statusText); - if (_parent->isMediaUnread()) { + if (_parent->data()->isMediaUnread()) { auto w = st::normalFont->width(statusText); if (w + st::mediaUnreadSkip + st::mediaUnreadSize <= statuswidth) { p.setPen(Qt::NoPen); @@ -1851,7 +1661,8 @@ HistoryTextState HistoryDocument::getState(QPoint point, HistoryStateRequest req auto waveformbottom = st::msgFilePadding.top() - topMinus + st::msgWaveformMax + st::msgWaveformMin; if (QRect(nameleft, nametop, namewidth, waveformbottom - nametop).contains(point)) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { + if (state.id == AudioMsgId(_data, _parent->data()->fullId()) + && !Media::Player::IsStoppedOrStopping(state.state)) { if (!voice->seeking()) { voice->setSeekingStart((point.x() - nameleft) / float64(namewidth)); } @@ -1895,31 +1706,11 @@ void HistoryDocument::updatePressed(QPoint point) { nameright = st::msgFilePadding.left(); } voice->setSeekingCurrent(snap((point.x() - nameleft) / float64(width() - nameleft - nameright), 0., 1.)); - Auth().data().requestItemRepaint(_parent); + Auth().data().requestViewRepaint(_parent); } } } -QString HistoryDocument::notificationText() const { - QString result; - buildStringRepresentation([&result](const QString &type, const QString &fileName, const Text &caption) { - result = WithCaptionNotificationText( - fileName.isEmpty() ? type : fileName, - caption); - }); - return result; -} - -QString HistoryDocument::inDialogsText() const { - QString result; - buildStringRepresentation([&result](const QString &type, const QString &fileName, const Text &caption) { - result = WithCaptionDialogsText( - fileName.isEmpty() ? type : fileName, - caption); - }); - return result; -} - TextSelection HistoryDocument::adjustSelection( TextSelection selection, TextSelectType type) const { @@ -1952,18 +1743,6 @@ TextWithEntities HistoryDocument::selectedText(TextSelection selection) const { return result; } -Storage::SharedMediaTypesMask HistoryDocument::sharedMediaTypes() const { - using Type = Storage::SharedMediaType; - if (_data->isVoiceMessage()) { - return Storage::SharedMediaTypesMask{} - .added(Type::VoiceFile) - .added(Type::RoundVoiceFile); - } else if (_data->isSharedMediaMusic()) { - return Type::MusicFile; - } - return Type::File; -} - template void HistoryDocument::buildStringRepresentation(Callback callback) const { const Text emptyCaption; @@ -2025,7 +1804,8 @@ bool HistoryDocument::updateStatusText() const { statusSize = FileStatusSizeLoaded; if (_data->isVoiceMessage()) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { + if (state.id == AudioMsgId(_data, _parent->data()->fullId()) + && !Media::Player::IsStoppedOrStopping(state.state)) { if (auto voice = Get()) { bool was = (voice->_playback != nullptr); voice->ensurePlayback(this); @@ -2050,18 +1830,19 @@ bool HistoryDocument::updateStatusText() const { voice->checkPlaybackFinished(); } } - if (!showPause && (state.id == AudioMsgId(_data, _parent->fullId()))) { + if (!showPause && (state.id == AudioMsgId(_data, _parent->data()->fullId()))) { showPause = Media::Player::instance()->isSeeking(AudioMsgId::Type::Voice); } } else if (_data->isAudioFile()) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { + if (state.id == AudioMsgId(_data, _parent->data()->fullId()) + && !Media::Player::IsStoppedOrStopping(state.state)) { statusSize = -1 - (state.position / state.frequency); realDuration = (state.length / state.frequency); showPause = (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); } else { } - if (!showPause && (state.id == AudioMsgId(_data, _parent->fullId()))) { + if (!showPause && (state.id == AudioMsgId(_data, _parent->data()->fullId()))) { showPause = Media::Player::instance()->isSeeking(AudioMsgId::Type::Song); } } @@ -2088,7 +1869,9 @@ void HistoryDocument::step_voiceProgress(float64 ms, bool timer) { } else { voice->_playback->a_progress.update(qMin(dt, 1.), anim::linear); } - if (timer) Auth().data().requestItemRepaint(_parent); + if (timer) { + Auth().data().requestViewRepaint(_parent); + } } } } @@ -2100,7 +1883,7 @@ void HistoryDocument::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool } else if (!pressed && voice->seeking()) { auto type = AudioMsgId::Type::Voice; auto state = Media::Player::mixer()->currentState(type); - if (state.id == AudioMsgId(_data, _parent->fullId()) && state.length) { + if (state.id == AudioMsgId(_data, _parent->data()->fullId()) && state.length) { auto currentProgress = voice->seekingCurrent(); auto currentPosition = state.frequency ? qRound(currentProgress * state.length * 1000. / state.frequency) @@ -2117,10 +1900,10 @@ void HistoryDocument::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool HistoryFileMedia::clickHandlerPressedChanged(p, pressed); } -void HistoryDocument::refreshParentId(not_null realParent) { +void HistoryDocument::refreshParentId(not_null realParent) { HistoryFileMedia::refreshParentId(realParent); - const auto contextId = realParent->fullId(); + const auto contextId = realParent->data()->fullId(); if (auto thumbed = Get()) { if (thumbed->_linksavel) { thumbed->_linksavel->setMessageId(contextId); @@ -2136,45 +1919,15 @@ void HistoryDocument::refreshParentId(not_null realParent) { bool HistoryDocument::playInline(bool autoplay) { if (_data->isVoiceMessage()) { - DocumentOpenClickHandler::doOpen(_data, _parent, ActionOnLoadPlayInline); + DocumentOpenClickHandler::doOpen(_data, _parent->data(), ActionOnLoadPlayInline); return true; } else if (_data->isAudioFile()) { - Media::Player::instance()->play(AudioMsgId(_data, _parent->fullId())); + Media::Player::instance()->play(AudioMsgId(_data, _parent->data()->fullId())); return true; } return false; } -void HistoryDocument::attachToParent() { - App::regDocumentItem(_data, _parent); -} - -void HistoryDocument::detachFromParent() { - App::unregDocumentItem(_data, _parent); -} - -void HistoryDocument::updateSentMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaDocument) { - auto &mediaDocument = media.c_messageMediaDocument(); - if (!mediaDocument.has_document() || mediaDocument.has_ttl_seconds()) { - LOG(("Api Error: Got MTPMessageMediaDocument without document or with ttl_seconds in HistoryDocument::updateSentMedia()")); - return; - } - App::feedDocument(mediaDocument.vdocument, _data); - if (!_data->data().isEmpty()) { - if (_data->isVoiceMessage()) { - Local::writeAudio(_data->mediaKey(), _data->data()); - } else { - Local::writeStickerImage(_data->mediaKey(), _data->data()); - } - } - } -} - -bool HistoryDocument::needReSetInlineResultMedia(const MTPMessageMedia &media) { - return needReSetInlineResultDocument(media, _data); -} - TextWithEntities HistoryDocument::getCaption() const { if (const auto captioned = Get()) { return captioned->_caption.originalTextWithEntities(); @@ -2187,13 +1940,14 @@ ImagePtr HistoryDocument::replyPreview() { } HistoryGif::HistoryGif( - not_null parent, + not_null parent, not_null document, const QString &caption) : HistoryFileMedia(parent) , _data(document) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { - setDocumentLinks(_data, parent, true); + const auto item = parent->data(); + setDocumentLinks(_data, item, true); setStatusSize(FileStatusSizeReady); @@ -2201,33 +1955,22 @@ HistoryGif::HistoryGif( _caption.setText( st::messageTextStyle, caption + _parent->skipBlock(), - Ui::ItemTextNoMonoOptions(_parent)); + Ui::ItemTextNoMonoOptions(item)); } _data->thumb->load(); } -HistoryGif::HistoryGif( - not_null parent, - const HistoryGif &other) -: HistoryFileMedia(parent) -, _data(other._data) -, _thumbw(other._thumbw) -, _thumbh(other._thumbh) -, _caption(other._caption) { - setDocumentLinks(_data, parent, true); - - setStatusSize(other._statusSize); -} - QSize HistoryGif::countOptimalSize() { if (_caption.hasSkipBlock()) { - _caption.setSkipBlock(_parent->skipBlockWidth(), _parent->skipBlockHeight()); + _caption.updateSkipBlock( + _parent->skipBlockWidth(), + _parent->skipBlockHeight()); } if (!_openInMediaviewLink) { _openInMediaviewLink = std::make_shared( _data, - _parent->fullId()); + _parent->data()->fullId()); } auto tw = 0; @@ -2277,9 +2020,10 @@ QSize HistoryGif::countOptimalSize() { } } } else if (isSeparateRoundVideo()) { - auto via = _parent->Get(); - auto reply = _parent->Get(); - auto forwarded = _parent->Get(); + const auto item = _parent->data(); + auto via = item->Get(); + auto reply = item->Get(); + auto forwarded = item->Get(); if (forwarded) { forwarded->create(via); } @@ -2327,7 +2071,7 @@ QSize HistoryGif::countCurrentSize(int newWidth) { if (_gif && _gif->ready()) { if (!_gif->started()) { auto isRound = _data->isVideoMessage(); - auto inWebPage = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); @@ -2345,9 +2089,10 @@ QSize HistoryGif::countCurrentSize(int newWidth) { } } } else if (isSeparateRoundVideo()) { - auto via = _parent->Get(); - auto reply = _parent->Get(); - auto forwarded = _parent->Get(); + const auto item = _parent->data(); + auto via = item->Get(); + auto reply = item->Get(); + auto forwarded = item->Get(); if (via || reply || forwarded) { auto additional = additionalWidth(via, reply, forwarded); newWidth += additional; @@ -2369,20 +2114,21 @@ QSize HistoryGif::countCurrentSize(int newWidth) { void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - _data->automaticLoad(_parent); + const auto item = _parent->data(); + _data->automaticLoad(item); auto loaded = _data->loaded(); - auto displayLoading = (_parent->id < 0) || _data->displayLoading(); + auto displayLoading = (item->id < 0) || _data->displayLoading(); auto selected = (selection == FullSelection); auto videoFinished = _gif && (_gif->mode() == Media::Clip::Reader::Mode::Video) && (_gif->state() == Media::Clip::State::Finished); if (loaded && cAutoPlayGif() && ((!_gif && !_gif.isBad()) || videoFinished)) { - Ui::autoplayMediaInlineAsync(_parent->fullId()); + Ui::autoplayMediaInlineAsync(item->fullId()); } auto paintx = 0, painty = 0, paintw = width(), painth = height(); bool bubble = _parent->hasBubble(); auto outbg = _parent->hasOutLayout(); - auto isChildMedia = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right(); @@ -2390,7 +2136,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM auto displayMute = false; auto animating = (_gif && _gif->started()); - if (!animating || _parent->id < 0) { + if (!animating || item->id < 0) { if (displayLoading) { ensureAnimation(); if (!_animation->radial.animating()) { @@ -2416,9 +2162,9 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM auto usex = 0, usew = paintw; auto separateRoundVideo = isSeparateRoundVideo(); - auto via = separateRoundVideo ? _parent->Get() : nullptr; - auto reply = separateRoundVideo ? _parent->Get() : nullptr; - auto forwarded = separateRoundVideo ? _parent->Get() : nullptr; + auto via = separateRoundVideo ? item->Get() : nullptr; + auto reply = separateRoundVideo ? item->Get() : nullptr; + auto forwarded = separateRoundVideo ? item->Get() : nullptr; if (via || reply || forwarded) { usew = maxWidth() - additionalWidth(via, reply, forwarded); if (outbg) { @@ -2429,8 +2175,8 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM QRect rthumb(rtlrect(usex + paintx, painty, usew, painth, width())); - auto roundRadius = isRound ? ImageRoundRadius::Ellipse : isChildMedia ? ImageRoundRadius::Small : ImageRoundRadius::Large; - auto roundCorners = (isRound || isChildMedia) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) + auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; + auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); if (animating) { auto paused = App::wnd()->controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any); @@ -2476,7 +2222,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM } if (radial || _gif.isBad() || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif()))) { - auto radialOpacity = (radial && loaded && _parent->id > 0) ? _animation->radial.opacity() : 1.; + auto radialOpacity = (radial && loaded && item->id > 0) ? _animation->radial.opacity() : 1.; auto inner = QRect(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); if (selected) { @@ -2496,17 +2242,17 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM } p.setOpacity(radialOpacity); - auto icon = ([this, radial, selected]() -> const style::icon * { + auto icon = [&]() -> const style::icon * { if (_data->loaded() && !radial) { return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); } else if (radial || _data->loading()) { - if (_parent->id > 0 || _data->uploading()) { + if (item->id > 0 || _data->uploading()) { return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); } return nullptr; } return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload); - })(); + }(); if (icon) { icon->paintInCenter(p, inner); } @@ -2516,7 +2262,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg); } - if (!isRound && (!animating || _parent->id < 0)) { + if (!isRound && (!animating || item->id < 0)) { auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x(); auto statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y(); auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); @@ -2536,13 +2282,13 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM (selected ? st::historyVideoMessageMuteSelected : st::historyVideoMessageMute).paintInCenter(p, muteRect); } - if (!isChildMedia && isRound) { - auto mediaUnread = _parent->isMediaUnread(); + if (!inWebPage && isRound) { + auto mediaUnread = item->isMediaUnread(); auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); auto statusX = usex + paintx + st::msgDateImgDelta + st::msgDateImgPadding.x(); auto statusY = painty + painth - st::msgDateImgDelta - statusH + st::msgDateImgPadding.y(); - if (_parent->isMediaUnread()) { + if (item->isMediaUnread()) { statusW += st::mediaUnreadSkip + st::mediaUnreadSize; } App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); @@ -2603,12 +2349,12 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM if (!isRound && !_caption.isEmpty()) { p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); _caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection); - } else if (!isChildMedia) { + } else if (!inWebPage) { auto fullRight = paintx + usex + usew; auto fullBottom = painty + painth; + auto maxRight = item->history()->width - st::msgMargin.left(); // #TODO view media - auto maxRight = _parent->history()->width - st::msgMargin.left(); - if (_parent->history()->canHaveFromPhotos()) { + if (item->history()->canHaveFromPhotos()) { maxRight -= st::msgMargin.right(); } else { maxRight -= st::msgMargin.left(); @@ -2663,13 +2409,14 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request) painth -= st::mediaCaptionSkip; } auto outbg = _parent->hasOutLayout(); - auto isChildMedia = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); auto isRound = _data->isVideoMessage(); auto usew = paintw, usex = 0; auto separateRoundVideo = isSeparateRoundVideo(); - auto via = separateRoundVideo ? _parent->Get() : nullptr; - auto reply = separateRoundVideo ? _parent->Get() : nullptr; - auto forwarded = separateRoundVideo ? _parent->Get() : nullptr; + const auto item = _parent->data(); + auto via = separateRoundVideo ? item->Get() : nullptr; + auto reply = separateRoundVideo ? item->Get() : nullptr; + auto forwarded = separateRoundVideo ? item->Get() : nullptr; if (via || reply || forwarded) { usew = maxWidth() - additionalWidth(via, reply, forwarded); if (outbg) { @@ -2748,8 +2495,8 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request) auto fullRight = usex + paintx + usew; auto fullBottom = painty + painth; // #TODO view media - auto maxRight = _parent->history()->width - st::msgMargin.left(); - if (_parent->history()->canHaveFromPhotos()) { + auto maxRight = _parent->data()->history()->width - st::msgMargin.left(); + if (_parent->data()->history()->canHaveFromPhotos()) { maxRight -= st::msgMargin.right(); } else { maxRight -= st::msgMargin.left(); @@ -2764,7 +2511,7 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request) fullRight = maxRight; } } - if (!isChildMedia) { + if (!inWebPage) { if (_parent->pointInTime(fullRight, fullBottom, point, isRound ? InfoDisplayOverBackground : InfoDisplayOverImage)) { result.cursor = HistoryInDateCursorState; } @@ -2784,14 +2531,6 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request) return result; } -QString HistoryGif::notificationText() const { - return WithCaptionNotificationText(mediaTypeString(), _caption); -} - -QString HistoryGif::inDialogsText() const { - return WithCaptionDialogsText(mediaTypeString(), _caption); -} - TextWithEntities HistoryGif::selectedText(TextSelection selection) const { return WithCaptionSelectedText(mediaTypeString(), _caption, selection); } @@ -2803,33 +2542,20 @@ bool HistoryGif::needsBubble() const { if (!_caption.isEmpty()) { return true; } - if (auto message = _parent->toHistoryMessage()) { - return message->viaBot() - || message->Has() - || message->displayForwardedFrom() -// || message->displayFromName() // #TODO media views - ; - } + const auto item = _parent->data(); + return item->viaBot() + || item->Has() + || _parent->displayForwardedFrom() + || _parent->displayFromName(); return false; } -Storage::SharedMediaTypesMask HistoryGif::sharedMediaTypes() const { - using Type = Storage::SharedMediaType; - if (_data->isVideoMessage()) { - return Storage::SharedMediaTypesMask{} - .added(Type::RoundFile) - .added(Type::RoundVoiceFile); - } else if (_data->isGifv()) { - return Type::GIF; - } - return Type::File; -} - int HistoryGif::additionalWidth() const { + const auto item = _parent->data(); return additionalWidth( - _parent->Get(), - _parent->Get(), - _parent->Get()); + item->Get(), + item->Get(), + item->Get()); } QString HistoryGif::mediaTypeString() const { @@ -2840,7 +2566,7 @@ QString HistoryGif::mediaTypeString() const { bool HistoryGif::isSeparateRoundVideo() const { return _data->isVideoMessage() - && (_parent->getMedia() == this) + && (_parent->media() == this) && !_parent->hasBubble(); } @@ -2904,29 +2630,6 @@ QString HistoryGif::additionalInfoString() const { return QString(); } -void HistoryGif::attachToParent() { - App::regDocumentItem(_data, _parent); -} - -void HistoryGif::detachFromParent() { - App::unregDocumentItem(_data, _parent); -} - -void HistoryGif::updateSentMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaDocument) { - auto &mediaDocument = media.c_messageMediaDocument(); - if (!mediaDocument.has_document() || mediaDocument.has_ttl_seconds()) { - LOG(("Api Error: Got MTPMessageMediaDocument without document or with ttl_seconds in HistoryGif::updateSentMedia()")); - return; - } - App::feedDocument(mediaDocument.vdocument, _data); - } -} - -bool HistoryGif::needReSetInlineResultMedia(const MTPMessageMedia &media) { - return needReSetInlineResultDocument(media, _data); -} - ImagePtr HistoryGif::replyPreview() { return _data->makeReplyPreview(); } @@ -2969,13 +2672,14 @@ bool HistoryGif::playInline(bool autoplay) { const auto mode = (!autoplay && _data->isVideoMessage()) ? Mode::Video : Mode::Gif; - setClipReader(Media::Clip::MakeReader(_data, _parent->fullId(), [this](Media::Clip::Notification notification) { - _parent->clipCallback(notification); + setClipReader(Media::Clip::MakeReader(_data, _parent->data()->fullId(), [this](Media::Clip::Notification notification) { + // #TODO GIFs + _parent->data()->clipCallback(notification); }, mode)); if (mode == Mode::Video) { _roundPlayback = std::make_unique(); _roundPlayback->setValueChangedCallback([this](float64 value) { - Auth().data().requestItemRepaint(_parent); + Auth().data().requestViewRepaint(_parent); }); if (App::main()) { App::main()->mediaMarkRead(_data); @@ -2999,8 +2703,8 @@ void HistoryGif::stopInline() { } clearClipReader(); - Auth().data().requestItemViewResize(_parent); - Auth().data().markItemLayoutChanged(_parent); + Auth().data().requestViewResize(_parent); + Auth().data().markViewLayoutChange(_parent); } void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) { @@ -3008,9 +2712,9 @@ void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) { App::unregGifItem(_gif.get()); } _gif = std::move(gif); - if (_gif) { - App::regGifItem(_gif.get(), _parent); - } + //if (_gif) { // #TODO GIFs + // App::regGifItem(_gif.get(), _parent); + //} } HistoryGif::~HistoryGif() { @@ -3018,19 +2722,19 @@ HistoryGif::~HistoryGif() { } float64 HistoryGif::dataProgress() const { - return (_data->uploading() || _parent->id > 0) + return (_data->uploading() || _parent->data()->id > 0) ? _data->progress() : 0; } bool HistoryGif::dataFinished() const { - return (_parent->id > 0) + return (_parent->data()->id > 0) ? (!_data->loading() && !_data->uploading()) : false; } bool HistoryGif::dataLoaded() const { - return (_parent->id > 0) ? _data->loaded() : false; + return (_parent->data()->id > 0) ? _data->loaded() : false; } bool HistoryGif::needInfoDisplay() const { @@ -3038,7 +2742,7 @@ bool HistoryGif::needInfoDisplay() const { } HistorySticker::HistorySticker( - not_null parent, + not_null parent, not_null document) : HistoryMedia(parent) , _data(document) @@ -3075,17 +2779,18 @@ QSize HistorySticker::countOptimalSize() { if (_pixh < 1) _pixh = 1; auto maxWidth = qMax(_pixw, st::minPhotoSize); auto minHeight = qMax(_pixh, st::minPhotoSize); - if (_parent->getMedia() == this) { + if (_parent->media() == this) { maxWidth += additionalWidth(); } return { maxWidth, minHeight }; } QSize HistorySticker::countCurrentSize(int newWidth) { + const auto item = _parent->data(); accumulate_min(newWidth, maxWidth()); - if (_parent->getMedia() == this) { - auto via = _parent->Get(); - auto reply = _parent->Get(); + if (_parent->media() == this) { + auto via = item->Get(); + auto reply = item->Get(); if (via || reply) { int usew = maxWidth() - additionalWidth(via, reply); int availw = newWidth - usew - st::msgReplyPadding.left() - st::msgReplyPadding.left() - st::msgReplyPadding.left(); @@ -3111,11 +2816,12 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T bool selected = (selection == FullSelection); auto outbg = _parent->hasOutLayout(); - auto childmedia = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); + const auto item = _parent->data(); int usew = maxWidth(), usex = 0; - auto via = childmedia ? nullptr : _parent->Get(); - auto reply = childmedia ? nullptr : _parent->Get(); + auto via = inWebPage ? nullptr : item->Get(); + auto reply = inWebPage ? nullptr : item->Get(); if (via || reply) { usew -= additionalWidth(via, reply); if (outbg) { @@ -3138,7 +2844,7 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T } } - if (!childmedia) { + if (!inWebPage) { auto fullRight = usex + usew; auto fullBottom = height(); _parent->drawInfo(p, fullRight, fullBottom, usex * 2 + usew, selected, InfoDisplayOverBackground); @@ -3188,11 +2894,12 @@ HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest requ } auto outbg = _parent->hasOutLayout(); - auto childmedia = (_parent->getMedia() != this); + auto inWebPage = (_parent->media() != this); + const auto item = _parent->data(); int usew = maxWidth(), usex = 0; - auto via = childmedia ? nullptr : _parent->Get(); - auto reply = childmedia ? nullptr : _parent->Get(); + auto via = inWebPage ? nullptr : item->Get(); + auto reply = inWebPage ? nullptr : item->Get(); if (via || reply) { usew -= additionalWidth(via, reply); if (outbg) { @@ -3231,7 +2938,7 @@ HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest requ } } } - if (_parent->getMedia() == this) { + if (_parent->media() == this) { auto fullRight = usex + usew; auto fullBottom = height(); if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) { @@ -3259,10 +2966,6 @@ QString HistorySticker::toString() const { return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji); } -QString HistorySticker::notificationText() const { - return toString(); -} - TextWithEntities HistorySticker::selectedText(TextSelection selection) const { if (selection != FullSelection) { return TextWithEntities(); @@ -3270,32 +2973,6 @@ TextWithEntities HistorySticker::selectedText(TextSelection selection) const { return { qsl("[ ") + toString() + qsl(" ]"), EntitiesInText() }; } -void HistorySticker::attachToParent() { - App::regDocumentItem(_data, _parent); -} - -void HistorySticker::detachFromParent() { - App::unregDocumentItem(_data, _parent); -} - -void HistorySticker::updateSentMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaDocument) { - auto &mediaDocument = media.c_messageMediaDocument(); - if (!mediaDocument.has_document() || mediaDocument.has_ttl_seconds()) { - LOG(("Api Error: Got MTPMessageMediaDocument without document or with ttl_seconds in HistorySticker::updateSentMedia()")); - return; - } - App::feedDocument(mediaDocument.vdocument, _data); - if (!_data->data().isEmpty()) { - Local::writeStickerImage(_data->mediaKey(), _data->data()); - } - } -} - -bool HistorySticker::needReSetInlineResultMedia(const MTPMessageMedia &media) { - return needReSetInlineResultDocument(media, _data); -} - ImagePtr HistorySticker::replyPreview() { return _data->makeReplyPreview(); } @@ -3312,9 +2989,10 @@ int HistorySticker::additionalWidth(const HistoryMessageVia *via, const HistoryM } int HistorySticker::additionalWidth() const { + const auto item = _parent->data(); return additionalWidth( - _parent->Get(), - _parent->Get()); + item->Get(), + item->Get()); } namespace { @@ -3327,16 +3005,15 @@ ClickHandlerPtr sendMessageClickHandler(PeerData *peer) { }); } -ClickHandlerPtr addContactClickHandler(HistoryItem *item) { +ClickHandlerPtr addContactClickHandler(not_null item) { return std::make_shared([fullId = item->fullId()] { - if (auto item = App::histItemById(fullId)) { - if (auto media = item->getMedia()) { - if (media->type() == MediaTypeContact) { - auto contact = static_cast(media); - auto fname = contact->fname(); - auto lname = contact->lname(); - auto phone = contact->phone(); - Ui::show(Box(fname, lname, phone)); + if (const auto item = App::histItemById(fullId)) { + if (const auto media = item->media()) { + if (const auto contact = media->sharedContact()) { + Ui::show(Box( + contact->firstName, + contact->lastName, + contact->phoneNumber)); } } } @@ -3345,7 +3022,13 @@ ClickHandlerPtr addContactClickHandler(HistoryItem *item) { } // namespace -HistoryContact::HistoryContact(not_null parent, int32 userId, const QString &first, const QString &last, const QString &phone) : HistoryMedia(parent) +HistoryContact::HistoryContact( + not_null parent, + UserId userId, + const QString &first, + const QString &last, + const QString &phone) +: HistoryMedia(parent) , _userId(userId) , _fname(first) , _lname(last) @@ -3358,6 +3041,7 @@ HistoryContact::HistoryContact(not_null parent, int32 userId, cons } QSize HistoryContact::countOptimalSize() { + const auto item = _parent->data(); auto maxWidth = st::msgFileMinWidth; _contact = _userId ? App::userLoaded(_userId) : nullptr; @@ -3365,7 +3049,7 @@ QSize HistoryContact::countOptimalSize() { _contact->loadUserpic(); } else { _photoEmpty = std::make_unique( - Data::PeerUserpicColor(_userId ? _userId : _parent->id), + Data::PeerUserpicColor(_userId ? _userId : _parent->data()->id), _name.originalText()); } if (_contact @@ -3373,7 +3057,7 @@ QSize HistoryContact::countOptimalSize() { _linkl = sendMessageClickHandler(_contact); _link = lang(lng_profile_send_message).toUpper(); } else if (_userId) { - _linkl = addContactClickHandler(_parent); + _linkl = addContactClickHandler(_parent->data()); _link = lang(lng_profile_add_contact).toUpper(); } _linkw = _link.isEmpty() ? 0 : st::semiboldFont->width(_link); @@ -3395,7 +3079,7 @@ QSize HistoryContact::countOptimalSize() { auto minHeight = 0; if (_userId) { minHeight = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom(); - if (_parent->Has()) { + if (item->Has()) { minHeight += st::msgDateFont->height - st::msgDateDelta.y(); } } else { @@ -3482,10 +3166,6 @@ HistoryTextState HistoryContact::getState(QPoint point, HistoryStateRequest requ return result; } -QString HistoryContact::notificationText() const { - return lang(lng_in_dlg_contact); -} - TextWithEntities HistoryContact::selectedText(TextSelection selection) const { if (selection != FullSelection) { return TextWithEntities(); @@ -3494,68 +3174,47 @@ TextWithEntities HistoryContact::selectedText(TextSelection selection) const { } void HistoryContact::attachToParent() { - if (_userId) { - App::regSharedContactItem(_userId, _parent); - } + //if (_userId) { + // App::regSharedContactItem(_userId, _parent); + //} // #TODO contacts } void HistoryContact::detachFromParent() { - if (_userId) { - App::unregSharedContactItem(_userId, _parent); - } -} - -void HistoryContact::updateSentMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaContact) { - if (_userId != media.c_messageMediaContact().vuser_id.v) { - detachFromParent(); - _userId = media.c_messageMediaContact().vuser_id.v; - attachToParent(); - } - } + //if (_userId) { + // App::unregSharedContactItem(_userId, _parent); + //} // #TODO contacts } HistoryContact::~HistoryContact() = default; -HistoryCall::HistoryCall(not_null parent, const MTPDmessageActionPhoneCall &call) : HistoryMedia(parent) -, _reason(GetReason(call)) { - if (_parent->out()) { - _text = lang(_reason == FinishReason::Missed ? lng_call_cancelled : lng_call_outgoing); - } else if (_reason == FinishReason::Missed) { - _text = lang(lng_call_missed); - } else if (_reason == FinishReason::Busy) { - _text = lang(lng_call_declined); - } else { - _text = lang(lng_call_incoming); - } - _duration = call.has_duration() ? call.vduration.v : 0; +HistoryCall::HistoryCall( + not_null parent, + not_null call) +: HistoryMedia(parent) { + _duration = call->duration; + _reason = call->finishReason; - _status = _parent->date.time().toString(cTimeFormat()); + const auto item = parent->data(); + _text = Data::MediaCall::Text(item, _reason); + _status = item->date.time().toString(cTimeFormat()); if (_duration) { - if (_reason != FinishReason::Missed && _reason != FinishReason::Busy) { - _status = lng_call_duration_info(lt_time, _status, lt_duration, formatDurationWords(_duration)); + if (_reason != FinishReason::Missed + && _reason != FinishReason::Busy) { + _status = lng_call_duration_info( + lt_time, + _status, + lt_duration, + formatDurationWords(_duration)); } else { _duration = 0; } } } -HistoryCall::FinishReason HistoryCall::GetReason(const MTPDmessageActionPhoneCall &call) { - if (call.has_reason()) { - switch (call.vreason.type()) { - case mtpc_phoneCallDiscardReasonBusy: return FinishReason::Busy; - case mtpc_phoneCallDiscardReasonDisconnect: return FinishReason::Disconnected; - case mtpc_phoneCallDiscardReasonHangup: return FinishReason::Hangup; - case mtpc_phoneCallDiscardReasonMissed: return FinishReason::Missed; - } - Unexpected("Call reason type."); - } - return FinishReason::Hangup; -} - QSize HistoryCall::countOptimalSize() { - _link = std::make_shared([peer = _parent->history()->peer] { - if (auto user = peer->asUser()) { + const auto user = _parent->data()->history()->peer->asUser(); + _link = std::make_shared([=] { + if (user) { Calls::Current().startOutgoingCall(user); } }); @@ -3615,14 +3274,6 @@ HistoryTextState HistoryCall::getState(QPoint point, HistoryStateRequest request return result; } -QString HistoryCall::notificationText() const { - auto result = _text; - if (_duration > 0) { - result = lng_call_type_and_duration(lt_type, result, lt_duration, formatDurationWords(_duration)); - } - return result; -} - TextWithEntities HistoryCall::selectedText(TextSelection selection) const { if (selection != FullSelection) { return TextWithEntities(); @@ -3648,27 +3299,15 @@ int unitedLineHeight() { } // namespace -HistoryWebPage::HistoryWebPage(not_null parent, not_null data) : HistoryMedia(parent) +HistoryWebPage::HistoryWebPage( + not_null parent, + not_null data) +: HistoryMedia(parent) , _data(data) , _title(st::msgMinWidth - st::webPageLeft) , _description(st::msgMinWidth - st::webPageLeft) { } -HistoryWebPage::HistoryWebPage( - not_null parent, - const HistoryWebPage &other) -: HistoryMedia(parent) -, _data(other._data) -, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr) -, _asArticle(other._asArticle) -, _title(other._title) -, _description(other._description) -, _siteNameWidth(other._siteNameWidth) -, _durationWidth(other._durationWidth) -, _pixw(other._pixw) -, _pixh(other._pixh) { -} - QSize HistoryWebPage::countOptimalSize() { if (_data->pendingTill) { return { 0, 0 }; @@ -3921,15 +3560,17 @@ QSize HistoryWebPage::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -TextSelection HistoryWebPage::toDescriptionSelection(TextSelection selection) const { - return internal::unshiftSelection(selection, _title); +TextSelection HistoryWebPage::toDescriptionSelection( + TextSelection selection) const { + return HistoryView::UnshiftItemSelection(selection, _title); } -TextSelection HistoryWebPage::fromDescriptionSelection(TextSelection selection) const { - return internal::shiftSelection(selection, _title); +TextSelection HistoryWebPage::fromDescriptionSelection( + TextSelection selection) const { + return HistoryView::ShiftItemSelection(selection, _title); } -void HistoryWebPage::refreshParentId(not_null realParent) { +void HistoryWebPage::refreshParentId(not_null realParent) { if (_attach) { _attach->refreshParentId(realParent); } @@ -4183,17 +3824,18 @@ void HistoryWebPage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool p } } bool HistoryWebPage::isDisplayed() const { + const auto item = _parent->data(); return !_data->pendingTill - && !_parent->Has(); + && !item->Has(); } void HistoryWebPage::attachToParent() { - App::regWebPageItem(_data, _parent); + //App::regWebPageItem(_data, _parent); // #TODO webpages if (_attach) _attach->attachToParent(); } void HistoryWebPage::detachFromParent() { - App::unregWebPageItem(_data, _parent); + //App::unregWebPageItem(_data, _parent); // #TODO webpages if (_attach) _attach->detachFromParent(); } @@ -4231,7 +3873,7 @@ QMargins HistoryWebPage::inBubblePadding() const { } bool HistoryWebPage::isLogEntryOriginal() const { - return _parent->isLogEntry() && _parent->getMedia() != this; + return _parent->data()->isLogEntry() && _parent->media() != this; } int HistoryWebPage::bottomInfoPadding() const { @@ -4249,34 +3891,32 @@ int HistoryWebPage::bottomInfoPadding() const { } HistoryGame::HistoryGame( - not_null parent, - not_null data) + not_null parent, + not_null data, + const TextWithEntities &consumed) : HistoryMedia(parent) , _data(data) , _title(st::msgMinWidth - st::webPageLeft) , _description(st::msgMinWidth - st::webPageLeft) { -} - -HistoryGame::HistoryGame( - not_null parent, - const HistoryGame &other) -: HistoryMedia(parent) -, _data(other._data) -, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr) -, _title(other._title) -, _description(other._description) { + if (!consumed.text.isEmpty()) { + _description.setMarkedText( + st::webPageDescriptionStyle, + consumed, + Ui::ItemTextOptions(parent->data())); + } } QSize HistoryGame::countOptimalSize() { auto lineHeight = unitedLineHeight(); - if (!_openl && IsServerMsgId(_parent->id)) { + const auto item = _parent->data(); + if (!_openl && IsServerMsgId(item->id)) { const auto row = 0; const auto column = 0; _openl = std::make_shared( row, column, - _parent->fullId()); + item->fullId()); } auto title = TextUtilities::SingleLine(_data->title); @@ -4363,9 +4003,9 @@ QSize HistoryGame::countOptimalSize() { return { maxWidth, minHeight }; } -void HistoryGame::refreshParentId(not_null realParent) { +void HistoryGame::refreshParentId(not_null realParent) { if (_openl) { - _openl->setMessageId(_parent->fullId()); + _openl->setMessageId(_parent->data()->fullId()); } if (_attach) { _attach->refreshParentId(realParent); @@ -4421,12 +4061,14 @@ QSize HistoryGame::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -TextSelection HistoryGame::toDescriptionSelection(TextSelection selection) const { - return internal::unshiftSelection(selection, _title); +TextSelection HistoryGame::toDescriptionSelection( + TextSelection selection) const { + return HistoryView::UnshiftItemSelection(selection, _title); } -TextSelection HistoryGame::fromDescriptionSelection(TextSelection selection) const { - return internal::shiftSelection(selection, _title); +TextSelection HistoryGame::fromDescriptionSelection( + TextSelection selection) const { + return HistoryView::ShiftItemSelection(selection, _title); } void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const { @@ -4550,7 +4192,7 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request tshift += _descriptionLines * lineHeight; } if (inThumb) { - if (!_parent->isLogEntry()) { + if (!_parent->data()->isLogEntry()) { result.link = _openl; } } else if (_attach) { @@ -4563,7 +4205,7 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request if (QRect(attachLeft, tshift, _attach->width(), height() - tshift - bshift).contains(point)) { if (_attach->isReadyForOpen()) { - if (!_parent->isLogEntry()) { + if (!_parent->data()->isLogEntry()) { result.link = _openl; } } else { @@ -4588,14 +4230,6 @@ TextSelection HistoryGame::adjustSelection(TextSelection selection, TextSelectTy return { titleSelection.from, fromDescriptionSelection(descriptionSelection).to }; } -bool HistoryGame::consumeMessageText(const TextWithEntities &textWithEntities) { - _description.setMarkedText( - st::webPageDescriptionStyle, - textWithEntities, - Ui::ItemTextOptions(_parent)); - return true; -} - void HistoryGame::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { if (_attach) { _attach->clickHandlerActiveChanged(p, active); @@ -4609,36 +4243,15 @@ void HistoryGame::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pres } void HistoryGame::attachToParent() { - App::regGameItem(_data, _parent); + // App::regGameItem(_data, _parent); // #TODO regitems if (_attach) _attach->attachToParent(); } void HistoryGame::detachFromParent() { - App::unregGameItem(_data, _parent); + // App::unregGameItem(_data, _parent); // #TODO regitems if (_attach) _attach->detachFromParent(); } -void HistoryGame::updateSentMedia(const MTPMessageMedia &media) { - if (media.type() == mtpc_messageMediaGame) { - auto &game = media.c_messageMediaGame().vgame; - if (game.type() == mtpc_game) { - App::feedGame(game.c_game(), _data); - } - } -} - -bool HistoryGame::needReSetInlineResultMedia(const MTPMessageMedia &media) { - updateSentMedia(media); - return false; -} - -QString HistoryGame::notificationText() const { - QString result; // add a game controller emoji before game title - result.reserve(_data->title.size() + 3); - result.append(QChar(0xD83C)).append(QChar(0xDFAE)).append(QChar(' ')).append(_data->title); - return result; -} - TextWithEntities HistoryGame::selectedText(TextSelection selection) const { if (selection == FullSelection) { return TextWithEntities(); @@ -4683,25 +4296,13 @@ int HistoryGame::bottomInfoPadding() const { } HistoryInvoice::HistoryInvoice( - not_null parent, - const MTPDmessageMediaInvoice &data) + not_null parent, + not_null invoice) : HistoryMedia(parent) , _title(st::msgMinWidth) , _description(st::msgMinWidth) , _status(st::msgMinWidth) { - fillFromData(data); -} - -HistoryInvoice::HistoryInvoice( - not_null parent, - const HistoryInvoice &other) -: HistoryMedia(parent) -, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr) -, _titleHeight(other._titleHeight) -, _descriptionHeight(other._descriptionHeight) -, _title(other._title) -, _description(other._description) -, _status(other._status) { + fillFromData(invoice); } QString HistoryInvoice::fillAmountAndCurrency(uint64 amount, const QString ¤cy) { @@ -4719,48 +4320,21 @@ QString HistoryInvoice::fillAmountAndCurrency(uint64 amount, const QString &curr //return currencyText + amountText; } -void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) { +void HistoryInvoice::fillFromData(not_null invoice) { // init attach - if (data.has_photo() && data.vphoto.type() == mtpc_webDocument) { - auto &doc = data.vphoto.c_webDocument(); - auto imageSize = QSize(); - for (auto &attribute : doc.vattributes.v) { - if (attribute.type() == mtpc_documentAttributeImageSize) { - auto &size = attribute.c_documentAttributeImageSize(); - imageSize = QSize(size.vw.v, size.vh.v); - break; - } - } - if (!imageSize.isEmpty()) { - auto thumbsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 100, 100); - auto thumb = ImagePtr(thumbsize.width(), thumbsize.height()); - - auto mediumsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 320, 320); - auto medium = ImagePtr(mediumsize.width(), mediumsize.height()); - - // We don't use size from WebDocument, because it is not reliable. - // It can be > 0 and different from the real size that we get in upload.WebFile result. - auto filesize = 0; // doc.vsize.v; - auto full = ImagePtr(WebFileImageLocation(imageSize.width(), imageSize.height(), doc.vdc_id.v, doc.vurl.v, doc.vaccess_hash.v), filesize); - auto photoId = rand_value(); - auto photo = App::photoSet(photoId, 0, 0, unixtime(), thumb, medium, full); - - _attach = std::make_unique(_parent, photo, QString()); - } - } - auto labelText = [&data] { - if (data.has_receipt_msg_id()) { - if (data.is_test()) { + auto labelText = [&] { + if (invoice->receiptMsgId) { + if (invoice->isTest) { return lang(lng_payments_receipt_label_test); } return lang(lng_payments_receipt_label); - } else if (data.is_test()) { + } else if (invoice->isTest) { return lang(lng_payments_invoice_label_test); } return lang(lng_payments_invoice_label); }; auto statusText = TextWithEntities { - fillAmountAndCurrency(data.vtotal_amount.v, qs(data.vcurrency)), + fillAmountAndCurrency(invoice->amount, invoice->currency), EntitiesInText() }; statusText.entities.push_back(EntityInText(EntityInTextBold, 0, statusText.text.size())); @@ -4768,14 +4342,13 @@ void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) { _status.setMarkedText( st::defaultTextStyle, statusText, - Ui::ItemTextOptions(_parent)); + Ui::ItemTextOptions(_parent->data())); - _receiptMsgId = data.has_receipt_msg_id() ? data.vreceipt_msg_id.v : 0; + _receiptMsgId = invoice->receiptMsgId; // init strings - auto description = qs(data.vdescription); - if (!description.isEmpty()) { - auto marked = TextWithEntities { description }; + if (!invoice->description.isEmpty()) { + auto marked = TextWithEntities { invoice->description }; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; TextUtilities::ParseEntities(marked, parseFlags); _description.setMarkedText( @@ -4783,11 +4356,10 @@ void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) { marked, Ui::WebpageTextDescriptionOptions()); } - auto title = TextUtilities::SingleLine(qs(data.vtitle)); - if (!title.isEmpty()) { + if (!invoice->title.isEmpty()) { _title.setText( st::webPageTitleStyle, - title, + invoice->title, Ui::WebpageTextTitleOptions()); } } @@ -4799,8 +4371,10 @@ QSize HistoryInvoice::countOptimalSize() { if (_status.hasSkipBlock()) { _status.removeSkipBlock(); } - } else if (!_status.hasSkipBlock()) { - _status.setSkipBlock(_parent->skipBlockWidth(), _parent->skipBlockHeight()); + } else { + _status.updateSkipBlock( + _parent->skipBlockWidth(), + _parent->skipBlockHeight()); } // init dimensions @@ -4889,15 +4463,17 @@ QSize HistoryInvoice::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -TextSelection HistoryInvoice::toDescriptionSelection(TextSelection selection) const { - return internal::unshiftSelection(selection, _title); +TextSelection HistoryInvoice::toDescriptionSelection( + TextSelection selection) const { + return HistoryView::UnshiftItemSelection(selection, _title); } -TextSelection HistoryInvoice::fromDescriptionSelection(TextSelection selection) const { - return internal::shiftSelection(selection, _title); +TextSelection HistoryInvoice::fromDescriptionSelection( + TextSelection selection) const { + return HistoryView::ShiftItemSelection(selection, _title); } -void HistoryInvoice::refreshParentId(not_null realParent) { +void HistoryInvoice::refreshParentId(not_null realParent) { if (_attach) { _attach->refreshParentId(realParent); } @@ -5070,10 +4646,6 @@ void HistoryInvoice::detachFromParent() { if (_attach) _attach->detachFromParent(); } -QString HistoryInvoice::notificationText() const { - return _title.originalText(); -} - TextWithEntities HistoryInvoice::selectedText(TextSelection selection) const { if (selection == FullSelection) { return TextWithEntities(); @@ -5106,11 +4678,16 @@ int HistoryInvoice::bottomInfoPadding() const { return result; } -HistoryLocation::HistoryLocation(not_null parent, const LocationCoords &coords, const QString &title, const QString &description) : HistoryMedia(parent) -, _data(App::location(coords)) +HistoryLocation::HistoryLocation( + not_null parent, + not_null location, + const QString &title, + const QString &description) +: HistoryMedia(parent) +, _data(location) , _title(st::msgMinWidth) , _description(st::msgMinWidth) -, _link(std::make_shared(coords)) { +, _link(std::make_shared(_data->coords)) { if (!title.isEmpty()) { _title.setText( st::webPageTitleStyle, @@ -5128,13 +4705,6 @@ HistoryLocation::HistoryLocation(not_null parent, const LocationCo } } -HistoryLocation::HistoryLocation(not_null parent, const HistoryLocation &other) : HistoryMedia(parent) -, _data(other._data) -, _title(other._title) -, _description(other._description) -, _link(std::make_shared(_data->coords)) { -} - QSize HistoryLocation::countOptimalSize() { auto tw = fullWidth(); auto th = fullHeight(); @@ -5198,12 +4768,14 @@ QSize HistoryLocation::countCurrentSize(int newWidth) { return { newWidth, newHeight }; } -TextSelection HistoryLocation::toDescriptionSelection(TextSelection selection) const { - return internal::unshiftSelection(selection, _title); +TextSelection HistoryLocation::toDescriptionSelection( + TextSelection selection) const { + return HistoryView::UnshiftItemSelection(selection, _title); } -TextSelection HistoryLocation::fromDescriptionSelection(TextSelection selection) const { - return internal::shiftSelection(selection, _title); +TextSelection HistoryLocation::fromDescriptionSelection( + TextSelection selection) const { + return HistoryView::ShiftItemSelection(selection, _title); } void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const { @@ -5265,7 +4837,7 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection, App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); } - if (_parent->getMedia() == this) { + if (_parent->media() == this) { auto fullRight = paintx + paintw; auto fullBottom = height(); _parent->drawInfo(p, fullRight, fullBottom, paintx * 2 + paintw, selected, InfoDisplayOverImage); @@ -5331,7 +4903,7 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req if (QRect(paintx, painty, paintw, painth).contains(point) && _data) { result.link = _link; } - if (_parent->getMedia() == this) { + if (_parent->media() == this) { auto fullRight = paintx + paintw; auto fullBottom = height(); if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) { @@ -5361,14 +4933,6 @@ TextSelection HistoryLocation::adjustSelection(TextSelection selection, TextSele return { titleSelection.from, fromDescriptionSelection(descriptionSelection).to }; } -QString HistoryLocation::notificationText() const { - return WithCaptionNotificationText(lang(lng_maps_point), _title); -} - -QString HistoryLocation::inDialogsText() const { - return WithCaptionDialogsText(lang(lng_maps_point), _title); -} - TextWithEntities HistoryLocation::selectedText(TextSelection selection) const { if (selection == FullSelection) { TextWithEntities result = { qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"), EntitiesInText() }; @@ -5397,13 +4961,11 @@ bool HistoryLocation::needsBubble() const { if (!_title.isEmpty() || !_description.isEmpty()) { return true; } - if (auto message = _parent->toHistoryMessage()) { - return message->viaBot() - || message->Has() - || message->displayForwardedFrom() -// || message->displayFromName() // #TODO media views -; - } + const auto item = _parent->data(); + return item->viaBot() + || item->Has() + || _parent->displayForwardedFrom() + || _parent->displayFromName(); return false; } diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index c6bd397019..2d9170e149 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -21,6 +21,12 @@ struct HistoryMessageVia; struct HistoryMessageReply; struct HistoryMessageForwarded; +namespace Data { +enum class CallFinishReason : char; +struct Invoice; +struct Call; +} // namespace Data + namespace Media { namespace Clip { class Playback; @@ -35,12 +41,6 @@ TextWithEntities WithCaptionSelectedText( const QString &attachType, const Text &caption, TextSelection selection); -QString WithCaptionNotificationText( - const QString &attachType, - const Text &caption); -QString WithCaptionDialogsText( - const QString &attachType, - const Text &caption); class HistoryFileMedia : public HistoryMedia { public: @@ -56,7 +56,7 @@ public: void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; - void refreshParentId(not_null realParent) override; + void refreshParentId(not_null realParent) override; bool allowsFastShare() const override { return true; @@ -128,22 +128,22 @@ protected: class HistoryPhoto : public HistoryFileMedia { public: HistoryPhoto( - not_null parent, + not_null parent, not_null photo, const QString &caption); HistoryPhoto( - not_null parent, + not_null parent, not_null chat, not_null photo, int width); HistoryPhoto( - not_null parent, + not_null parent, not_null chat, const MTPDphoto &photo, int width); HistoryPhoto( - not_null parent, - not_null realParent, + not_null parent, + not_null realParent, const HistoryPhoto &other); void init(); @@ -151,8 +151,8 @@ public: return MediaTypePhoto; } std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { + not_null newParent, + not_null realParent) const override { return std::make_unique(newParent, realParent, *this); } @@ -171,12 +171,8 @@ public: return !_caption.isEmpty(); } - QString notificationText() const override; - QString inDialogsText() const override; TextWithEntities selectedText(TextSelection selection) const override; - Storage::SharedMediaTypesMask sharedMediaTypes() const override; - PhotoData *getPhoto() const override { return _data; } @@ -199,12 +195,6 @@ public: QPoint point, HistoryStateRequest request) const override; - void updateSentMedia(const MTPMessageMedia &media) override; - bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; - - void attachToParent() override; - void detachFromParent() override; - bool hasReplyPreview() const override { return !_data->thumb->isNull(); } @@ -220,9 +210,6 @@ public: bool skipBubbleTail() const override { return isBubbleBottom() && _caption.isEmpty(); } - bool canEditCaption() const override { - return true; - } bool isReadyForOpen() const override { return _data->loaded(); } @@ -254,20 +241,20 @@ private: class HistoryVideo : public HistoryFileMedia { public: HistoryVideo( - not_null parent, + not_null parent, not_null document, const QString &caption); HistoryVideo( - not_null parent, - not_null realParent, + not_null parent, + not_null realParent, const HistoryVideo &other); HistoryMediaType type() const override { return MediaTypeVideo; } std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { + not_null newParent, + not_null realParent) const override { return std::make_unique(newParent, realParent, *this); } @@ -286,12 +273,8 @@ public: return !_caption.isEmpty(); } - QString notificationText() const override; - QString inDialogsText() const override; TextWithEntities selectedText(TextSelection selection) const override; - Storage::SharedMediaTypesMask sharedMediaTypes() const override; - DocumentData *getDocument() const override { return _data; } @@ -318,12 +301,6 @@ public: return _data->uploading(); } - void attachToParent() override; - void detachFromParent() override; - - void updateSentMedia(const MTPMessageMedia &media) override; - bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; - bool hasReplyPreview() const override { return !_data->thumb->isNull(); } @@ -339,9 +316,6 @@ public: bool skipBubbleTail() const override { return isBubbleBottom() && _caption.isEmpty(); } - bool canEditCaption() const override { - return true; - } protected: float64 dataProgress() const override; @@ -366,15 +340,14 @@ private: }; -class HistoryDocument : public HistoryFileMedia, public RuntimeComposer { +class HistoryDocument + : public HistoryFileMedia + , public RuntimeComposer { public: HistoryDocument( - not_null parent, + not_null parent, not_null document, const QString &caption); - HistoryDocument( - not_null parent, - const HistoryDocument &other); HistoryMediaType type() const override { return _data->isVoiceMessage() @@ -383,13 +356,6 @@ public: ? MediaTypeMusicFile : MediaTypeFile); } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - - return std::make_unique(newParent, *this); - } void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -401,12 +367,8 @@ public: uint16 fullSelectionLength() const override; bool hasTextForCopy() const override; - QString notificationText() const override; - QString inDialogsText() const override; TextWithEntities selectedText(TextSelection selection) const override; - Storage::SharedMediaTypesMask sharedMediaTypes() const override; - bool uploading() const override { return _data->uploading(); } @@ -417,12 +379,6 @@ public: bool playInline(bool autoplay) override; - void attachToParent() override; - void detachFromParent() override; - - void updateSentMedia(const MTPMessageMedia &media) override; - bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; - bool hasReplyPreview() const override { return !_data->thumb->isNull(); } @@ -439,15 +395,12 @@ public: bool hideForwardedFrom() const override { return _data->isSong(); } - bool canEditCaption() const override { - return true; - } void step_voiceProgress(float64 ms, bool timer); void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; - void refreshParentId(not_null realParent) override; + void refreshParentId(not_null realParent) override; protected: float64 dataProgress() const override; @@ -476,21 +429,13 @@ private: class HistoryGif : public HistoryFileMedia { public: HistoryGif( - not_null parent, + not_null parent, not_null document, const QString &caption); - HistoryGif(not_null parent, const HistoryGif &other); HistoryMediaType type() const override { return MediaTypeGif; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - - return std::make_unique(newParent, *this); - } void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -507,12 +452,8 @@ public: return !_caption.isEmpty(); } - QString notificationText() const override; - QString inDialogsText() const override; TextWithEntities selectedText(TextSelection selection) const override; - Storage::SharedMediaTypesMask sharedMediaTypes() const override; - bool uploading() const override { return _data->uploading(); } @@ -528,12 +469,6 @@ public: void stopInline() override; bool isRoundVideoPlaying() const override; - void attachToParent() override; - void detachFromParent() override; - - void updateSentMedia(const MTPMessageMedia &media) override; - bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; - bool hasReplyPreview() const override { return !_data->thumb->isNull(); } @@ -551,9 +486,6 @@ public: bool skipBubbleTail() const override { return isBubbleBottom() && _caption.isEmpty(); } - bool canEditCaption() const override { - return !_data->isVideoMessage(); - } bool isReadyForOpen() const override { return _data->loaded(); } @@ -600,19 +532,12 @@ private: class HistorySticker : public HistoryMedia { public: HistorySticker( - not_null parent, + not_null parent, not_null document); HistoryMediaType type() const override { return MediaTypeSticker; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - - return std::make_unique(newParent, _data); - } void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -627,19 +552,12 @@ public: return true; } - QString notificationText() const override; TextWithEntities selectedText(TextSelection selection) const override; DocumentData *getDocument() const override { return _data; } - void attachToParent() override; - void detachFromParent() override; - - void updateSentMedia(const MTPMessageMedia &media) override; - bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; - bool hasReplyPreview() const override { return !_data->thumb->isNull(); } @@ -674,8 +592,8 @@ private: class HistoryContact : public HistoryMedia { public: HistoryContact( - not_null parent, - int32 userId, + not_null parent, + UserId userId, const QString &first, const QString &last, const QString &phone); @@ -683,18 +601,6 @@ public: HistoryMediaType type() const override { return MediaTypeContact; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - - return std::make_unique( - newParent, - _userId, - _fname, - _lname, - _phone); - } void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -706,14 +612,11 @@ public: return true; } - QString notificationText() const override; TextWithEntities selectedText(TextSelection selection) const override; void attachToParent() override; void detachFromParent() override; - void updateSentMedia(const MTPMessageMedia &media) override; - bool needsBubble() const override { return true; } @@ -736,7 +639,7 @@ public: private: QSize countOptimalSize() override; - int32 _userId = 0; + UserId _userId = 0; UserData *_contact = nullptr; int _phonew = 0; @@ -753,17 +656,12 @@ private: class HistoryCall : public HistoryMedia { public: HistoryCall( - not_null parent, - const MTPDmessageActionPhoneCall &call); + not_null parent, + not_null call); HistoryMediaType type() const override { return MediaTypeCall; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Unexpected("Clone HistoryCall."); - } void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -775,7 +673,6 @@ public: return false; } - QString notificationText() const override; TextWithEntities selectedText(TextSelection selection) const override; bool needsBubble() const override { @@ -785,22 +682,14 @@ public: return true; } - enum class FinishReason { - Missed, - Busy, - Disconnected, - Hangup, - }; - FinishReason reason() const { - return _reason; - } + Data::CallFinishReason reason() const; private: + using FinishReason = Data::CallFinishReason; + QSize countOptimalSize() override; - static FinishReason GetReason(const MTPDmessageActionPhoneCall &call); - - FinishReason _reason = FinishReason::Missed; + FinishReason _reason; int _duration = 0; QString _text; @@ -813,24 +702,14 @@ private: class HistoryWebPage : public HistoryMedia { public: HistoryWebPage( - not_null parent, + not_null parent, not_null data); - HistoryWebPage( - not_null parent, - const HistoryWebPage &other); HistoryMediaType type() const override { return MediaTypeWebPage; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - return std::make_unique(newParent, *this); - } - - void refreshParentId(not_null realParent) override; + void refreshParentId(not_null realParent) override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -930,21 +809,16 @@ private: class HistoryGame : public HistoryMedia { public: - HistoryGame(not_null parent, not_null data); - HistoryGame(not_null parent, const HistoryGame &other); + HistoryGame( + not_null parent, + not_null data, + const TextWithEntities &consumed); HistoryMediaType type() const override { return MediaTypeGame; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - return std::make_unique(newParent, *this); - } - - void refreshParentId(not_null realParent) override; + void refreshParentId(not_null realParent) override; void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -961,7 +835,6 @@ public: bool hasTextForCopy() const override { return false; // we do not add _title and _description in FullSelection text copy. } - bool consumeMessageText(const TextWithEntities &textWithEntities) override; bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { return _attach && _attach->toggleSelectionByHandlerClick(p); @@ -970,7 +843,6 @@ public: return _attach && _attach->dragItemByHandler(p); } - QString notificationText() const override; TextWithEntities selectedText(TextSelection selection) const override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; @@ -1004,9 +876,6 @@ public: return _data; } - void updateSentMedia(const MTPMessageMedia &media) override; - bool needReSetInlineResultMedia(const MTPMessageMedia &media) override; - bool needsBubble() const override { return true; } @@ -1045,24 +914,14 @@ private: class HistoryInvoice : public HistoryMedia { public: HistoryInvoice( - not_null parent, - const MTPDmessageMediaInvoice &data); - HistoryInvoice( - not_null parent, - const HistoryInvoice &other); + not_null parent, + not_null invoice); HistoryMediaType type() const override { return MediaTypeInvoice; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - return std::make_unique(newParent, *this); - } - - void refreshParentId(not_null realParent) override; + void refreshParentId(not_null realParent) override; MsgId getReceiptMsgId() const { return _receiptMsgId; @@ -1092,7 +951,6 @@ public: return _attach && _attach->dragItemByHandler(p); } - QString notificationText() const override; TextWithEntities selectedText(TextSelection selection) const override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; @@ -1123,7 +981,7 @@ private: QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; - void fillFromData(const MTPDmessageMediaInvoice &data); + void fillFromData(not_null invoice); TextSelection toDescriptionSelection(TextSelection selection) const; TextSelection fromDescriptionSelection(TextSelection selection) const; @@ -1148,24 +1006,14 @@ struct LocationData; class HistoryLocation : public HistoryMedia { public: HistoryLocation( - not_null parent, - const LocationCoords &coords, + not_null parent, + not_null location, const QString &title = QString(), const QString &description = QString()); - HistoryLocation( - not_null parent, - const HistoryLocation &other); HistoryMediaType type() const override { return MediaTypeLocation; } - std::unique_ptr clone( - not_null newParent, - not_null realParent) const override { - Expects(newParent == realParent); - - return std::make_unique(newParent, *this); - } void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; @@ -1187,8 +1035,6 @@ public: return p == _link; } - QString notificationText() const override; - QString inDialogsText() const override; TextWithEntities selectedText(TextSelection selection) const override; bool needsBubble() const override; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index d741e8f34e..8e230b5480 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -24,15 +24,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text_options.h" #include "messenger.h" #include "layout.h" -#include "styles/style_dialogs.h" -#include "styles/style_widgets.h" -#include "styles/style_history.h" -#include "styles/style_window.h" #include "window/notifications_manager.h" #include "window/window_controller.h" #include "observer_peer.h" #include "storage/storage_shared_media.h" #include "data/data_session.h" +#include "data/data_media_types.h" +#include "styles/style_dialogs.h" +#include "styles/style_widgets.h" +#include "styles/style_history.h" +#include "styles/style_window.h" namespace { @@ -49,23 +50,17 @@ MTPDmessage::Flags NewForwardedFlags( if (fwd->Has()) { result |= MTPDmessage::Flag::f_via_bot_id; } - if (auto channel = peer->asChannel()) { - if (auto media = fwd->getMedia()) { - if (media->type() == MediaTypeWebPage) { - // Drop web page if we're not allowed to send it. - if (channel->restricted( - ChannelRestriction::f_embed_links)) { - result &= MTPDmessage::Flag::f_media; - } + if (const auto channel = peer->asChannel()) { + if (dynamic_cast(fwd->media())) { + // Drop web page if we're not allowed to send it. + if (channel->restricted( + ChannelRestriction::f_embed_links)) { + result &= ~MTPDmessage::Flag::f_media; } } - } else { - if (auto media = fwd->getMedia()) { - if (media->type() == MediaTypeVoiceFile) { - result |= MTPDmessage::Flag::f_media_unread; -// } else if (media->type() == MediaTypeVideo) { -// result |= MTPDmessage::flag_media_unread; - } + } else if (const auto media = fwd->media()) { + if (media->forwardedBecomesUnread()) { + result |= MTPDmessage::Flag::f_media_unread; } } if (fwd->hasViews()) { @@ -74,56 +69,6 @@ MTPDmessage::Flags NewForwardedFlags( return result; } -bool HasMediaItems(const HistoryItemsList &items) { - for (const auto item : items) { - if (const auto media = item->getMedia()) { - switch (media->type()) { - case MediaTypePhoto: - case MediaTypeVideo: - case MediaTypeGrouped: - case MediaTypeFile: - case MediaTypeMusicFile: - case MediaTypeVoiceFile: return true; - case MediaTypeGif: return media->getDocument()->isVideoMessage(); - } - } - } - return false; -} - -bool HasStickerItems(const HistoryItemsList &items) { - for (const auto item : items) { - if (const auto media = item->getMedia()) { - switch (media->type()) { - case MediaTypeSticker: return true; - } - } - } - return false; -} - -bool HasGifItems(const HistoryItemsList &items) { - for (const auto item : items) { - if (const auto media = item->getMedia()) { - switch (media->type()) { - case MediaTypeGif: return !media->getDocument()->isVideoMessage(); - } - } - } - return false; -} - -bool HasGameItems(const HistoryItemsList &items) { - for (const auto item : items) { - if (const auto media = item->getMedia()) { - switch (media->type()) { - case MediaTypeGame: return true; - } - } - } - return false; -} - bool HasInlineItems(const HistoryItemsList &items) { for (const auto item : items) { if (item->viaBot()) { @@ -148,10 +93,10 @@ void FastShareMessage(not_null item) { const auto data = std::make_shared( item->history()->peer, Auth().data().itemOrItsGroup(item)); - const auto isGroup = (item->getFullGroup() != nullptr); + const auto isGroup = (Auth().data().groups().find(item) != nullptr); const auto isGame = item->getMessageBot() - && item->getMedia() - && (item->getMedia()->type() == MediaTypeGame); + && item->media() + && (item->media()->game() != nullptr); const auto canCopyLink = item->hasDirectLink() || isGame; auto copyCallback = [data]() { @@ -161,12 +106,15 @@ void FastShareMessage(not_null item) { QApplication::clipboard()->setText(item->directLink()); Ui::Toast::Show(lang(lng_channel_public_link_copied)); - } else if (auto bot = item->getMessageBot()) { - if (auto media = item->getMedia()) { - if (media->type() == MediaTypeGame) { - auto shortName = static_cast(media)->game()->shortName; + } else if (const auto bot = item->getMessageBot()) { + if (const auto media = item->media()) { + if (const auto game = media->game()) { + const auto link = Messenger::Instance().createInternalLinkFull( + bot->username + + qsl("?game=") + + game->shortName); - QApplication::clipboard()->setText(Messenger::Instance().createInternalLinkFull(bot->username + qsl("?game=") + shortName)); + QApplication::clipboard()->setText(link); Ui::Toast::Show(lang(lng_share_game_link_copied)); } @@ -297,15 +245,16 @@ QString GetErrorTextForForward( } if (auto megagroup = peer->asMegagroup()) { - if (megagroup->restricted(ChannelRestriction::f_send_media) && HasMediaItems(items)) { - return lang(lng_restricted_send_media); - } else if (megagroup->restricted(ChannelRestriction::f_send_stickers) && HasStickerItems(items)) { - return lang(lng_restricted_send_stickers); - } else if (megagroup->restricted(ChannelRestriction::f_send_gifs) && HasGifItems(items)) { - return lang(lng_restricted_send_gifs); - } else if (megagroup->restricted(ChannelRestriction::f_send_games) && HasGameItems(items)) { - return lang(lng_restricted_send_inline); - } else if (megagroup->restricted(ChannelRestriction::f_send_inline) && HasInlineItems(items)) { + for (const auto item : items) { + if (const auto media = item->media()) { + const auto error = media->errorTextForForward(megagroup); + if (!error.isEmpty() && error != qstr("skip")) { + return error; + } + } + } + if (megagroup->restricted(ChannelRestriction::f_send_inline) + && HasInlineItems(items)) { return lang(lng_restricted_send_inline); } } @@ -324,7 +273,6 @@ struct HistoryMessage::CreateConfig { QString authorOriginal; QDateTime originalDate; QDateTime editDate; - MessageGroupId groupId = MessageGroupId::None; // For messages created from MTP structs. const MTPReplyMarkup *mtpMarkup = nullptr; @@ -361,7 +309,7 @@ HistoryMessage::HistoryMessage( if (msg.has_edit_date()) config.editDate = ::date(msg.vedit_date); if (msg.has_post_author()) config.author = qs(msg.vpost_author); if (msg.has_grouped_id()) { - config.groupId = MessageGroupId::FromRaw(msg.vgrouped_id.v); + setGroupId(MessageGroupId::FromRaw(msg.vgrouped_id.v)); } createComponents(config); @@ -387,7 +335,9 @@ HistoryMessage::HistoryMessage( switch (msg.vaction.type()) { case mtpc_messageActionPhoneCall: { - _media = std::make_unique(this, msg.vaction.c_messageActionPhoneCall()); + _media = std::make_unique( + this, + msg.vaction.c_messageActionPhoneCall()); } break; default: Unexpected("Service message action type in HistoryMessage."); @@ -443,26 +393,25 @@ HistoryMessage::HistoryMessage( } // Copy inline keyboard when forwarding messages with a game. - auto mediaOriginal = fwd->getMedia(); - auto mediaType = mediaOriginal ? mediaOriginal->type() : MediaTypeCount; - if (mediaOriginal && mediaType == MediaTypeGame) { + auto mediaOriginal = fwd->media(); + if (mediaOriginal && mediaOriginal->game()) { config.inlineMarkup = fwd->inlineReplyMarkup(); } createComponents(config); - auto cloneMedia = [this, history, mediaType] { - if (mediaType == MediaTypeWebPage) { - if (auto channel = history->peer->asChannel()) { + auto ignoreMedia = [&] { + if (mediaOriginal && mediaOriginal->webpage()) { + if (const auto channel = history->peer->asChannel()) { if (channel->restricted(ChannelRestriction::f_embed_links)) { - return false; + return true; } } } - return (mediaType != MediaTypeCount); + return false; }; - if (cloneMedia()) { - _media = mediaOriginal->clone(this, this); + if (mediaOriginal && !ignoreMedia()) { + _media = mediaOriginal->clone(this); } setText(fwd->originalText()); } @@ -498,7 +447,7 @@ HistoryMessage::HistoryMessage( : HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup); - initMediaFromDocument(document, caption); + _media = std::make_unique(this, document, caption); setText(TextWithEntities()); } @@ -517,7 +466,7 @@ HistoryMessage::HistoryMessage( : HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup); - _media = std::make_unique(this, photo, caption); + _media = std::make_unique(this, photo, caption); setText(TextWithEntities()); } @@ -535,7 +484,7 @@ HistoryMessage::HistoryMessage( : HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { createComponentsHelper(flags, replyTo, viaBotId, postAuthor, markup); - _media = std::make_unique(this, game); + _media = std::make_unique(this, game); setText(TextWithEntities()); } @@ -605,73 +554,54 @@ void HistoryMessage::applyGroupAdminChanges( } } -bool HistoryMessage::displayEditedBadge() const { - return !displayedEditDate().isNull(); +bool HistoryMessage::allowsForward() const { + if (id < 0 || isLogEntry()) { + return false; + } + return !_media || _media->allowsForward(); } -QDateTime HistoryMessage::displayedEditDate() const { - auto hasViaBotId = Has(); - auto hasInlineMarkup = (inlineReplyMarkup() != nullptr); - return displayedEditDate(hasViaBotId || hasInlineMarkup); -} +bool HistoryMessage::allowsEdit(const QDateTime &now) const { + const auto peer = _history->peer; + const auto messageToMyself = peer->isSelf(); + const auto canPinInMegagroup = [&] { + if (const auto megagroup = peer->asMegagroup()) { + return megagroup->canPinMessages(); + } + return false; + }(); + const auto messageTooOld = (messageToMyself || canPinInMegagroup) + ? false + : (date.secsTo(now) >= Global::EditTimeLimit()); + if (id < 0 || messageTooOld) { + return false; + } -QDateTime HistoryMessage::displayedEditDate( - bool hasViaBotOrInlineMarkup) const { - if (hasViaBotOrInlineMarkup) { - return QDateTime(); - } else if (const auto fromUser = from()->asUser()) { - if (fromUser->botInfo) { - return QDateTime(); + if (Has() || Has()) { + return false; + } + + if (_media && !_media->allowsEdit()) { + return false; + } + if (messageToMyself) { + return true; + } + if (const auto channel = _history->peer->asChannel()) { + if (isPost() && channel->canEditMessages()) { + return true; + } + if (out()) { + return !isPost() || channel->canPublish(); } } - if (const auto edited = displayedEditBadge()) { - return edited->date; - } - return QDateTime(); -} - -HistoryMessageEdited *HistoryMessage::displayedEditBadge() { - if (_media && _media->overrideEditedDate()) { - return _media->displayedEditBadge(); - } - return Get(); -} - -const HistoryMessageEdited *HistoryMessage::displayedEditBadge() const { - if (_media && _media->overrideEditedDate()) { - return _media->displayedEditBadge(); - } - return Get(); + return out(); } bool HistoryMessage::uploading() const { return _media && _media->uploading(); } -bool HistoryMessage::displayRightAction() const { - return displayFastShare() || displayGoToOriginal(); -} - -bool HistoryMessage::displayFastShare() const { - if (_history->peer->isChannel()) { - return !_history->peer->isMegagroup(); - } else if (auto user = _history->peer->asUser()) { - if (user->botInfo && !out()) { - return _media && _media->allowsFastShare(); - } - } - return false; -} - -bool HistoryMessage::displayGoToOriginal() const { - if (_history->peer->isSelf()) { - if (auto forwarded = Get()) { - return forwarded->savedFromPeer && forwarded->savedFromMsgId; - } - } - return false; -} - void HistoryMessage::createComponents(const CreateConfig &config) { uint64 mask = 0; if (config.replyTo) { @@ -708,9 +638,6 @@ void HistoryMessage::createComponents(const CreateConfig &config) { } else if (config.inlineMarkup) { mask |= HistoryMessageReplyMarkup::Bit(); } - if (config.groupId) { - mask |= HistoryMessageGroup::Bit(); - } UpdateComponents(mask); @@ -753,14 +680,10 @@ void HistoryMessage::createComponents(const CreateConfig &config) { _flags |= MTPDmessage_ClientFlag::f_has_switch_inline_button; } } - if (const auto group = Get()) { - group->groupId = config.groupId; - group->leader = this; - } _fromNameVersion = displayFrom()->nameVersion; } -QString formatViewsCount(int32 views) { +QString FormatViewsCount(int views) { if (views > 999999) { views /= 100000; if (views % 10) { @@ -779,48 +702,30 @@ QString formatViewsCount(int32 views) { return qsl("1"); } -void HistoryMessage::initTime() { - if (const auto msgsigned = Get()) { - _timeWidth = msgsigned->maxWidth(); - } else if (const auto edited = displayedEditBadge()) { - _timeWidth = edited->maxWidth(); - } else { - _timeText = date.toString(cTimeFormat()); - _timeWidth = st::msgDateFont->width(_timeText); - } - if (const auto views = Get()) { - views->_viewsText = (views->_views >= 0) ? formatViewsCount(views->_views) : QString(); - views->_viewsWidth = views->_viewsText.isEmpty() ? 0 : st::msgDateFont->width(views->_viewsText); - } - if (_text.hasSkipBlock()) { - _text.setSkipBlock(skipBlockWidth(), skipBlockHeight()); - _textWidth = -1; - _textHeight = 0; - } -} - void HistoryMessage::initMedia(const MTPMessageMedia *media) { + _media = nullptr; + switch (media ? media->type() : mtpc_messageMediaEmpty) { case mtpc_messageMediaContact: { auto &d = media->c_messageMediaContact(); - _media = std::make_unique(this, d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number)); + _media = std::make_unique(this, d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number)); } break; case mtpc_messageMediaGeo: { auto &point = media->c_messageMediaGeo().vgeo; if (point.type() == mtpc_geoPoint) { - _media = std::make_unique(this, LocationCoords(point.c_geoPoint())); + _media = std::make_unique(this, LocationCoords(point.c_geoPoint())); } } break; case mtpc_messageMediaGeoLive: { auto &point = media->c_messageMediaGeoLive().vgeo; if (point.type() == mtpc_geoPoint) { - _media = std::make_unique(this, LocationCoords(point.c_geoPoint())); + _media = std::make_unique(this, LocationCoords(point.c_geoPoint())); } } break; case mtpc_messageMediaVenue: { auto &d = media->c_messageMediaVenue(); if (d.vgeo.type() == mtpc_geoPoint) { - _media = std::make_unique(this, LocationCoords(d.vgeo.c_geoPoint()), qs(d.vtitle), qs(d.vaddress)); + _media = std::make_unique(this, LocationCoords(d.vgeo.c_geoPoint()), qs(d.vtitle), qs(d.vaddress)); } } break; case mtpc_messageMediaPhoto: { @@ -828,7 +733,10 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media) { if (photo.has_ttl_seconds()) { LOG(("App Error: Unexpected MTPMessageMediaPhoto with ttl_seconds in HistoryMessage.")); } else if (photo.has_photo() && photo.vphoto.type() == mtpc_photo) { - _media = std::make_unique(this, App::feedPhoto(photo.vphoto.c_photo()), photo.has_caption() ? qs(photo.vcaption) : QString()); + _media = std::make_unique( + this, + App::feedPhoto(photo.vphoto.c_photo()), + photo.has_caption() ? qs(photo.vcaption) : QString()); } else { LOG(("API Error: Got MTPMessageMediaPhoto without photo and without ttl_seconds.")); } @@ -838,7 +746,10 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media) { if (document.has_ttl_seconds()) { LOG(("App Error: Unexpected MTPMessageMediaDocument with ttl_seconds in HistoryMessage.")); } else if (document.has_document() && document.vdocument.type() == mtpc_document) { - return initMediaFromDocument(App::feedDocument(document.vdocument.c_document()), document.has_caption() ? qs(document.vcaption) : QString()); + _media = std::make_unique( + this, + App::feedDocument(document.vdocument.c_document()), + document.has_caption() ? qs(document.vcaption) : QString()); } else { LOG(("API Error: Got MTPMessageMediaDocument without document and without ttl_seconds.")); } @@ -848,10 +759,10 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media) { switch (d.type()) { case mtpc_webPageEmpty: break; case mtpc_webPagePending: { - _media = std::make_unique(this, App::feedWebPage(d.c_webPagePending())); + _media = std::make_unique(this, App::feedWebPage(d.c_webPagePending())); } break; case mtpc_webPage: { - _media = std::make_unique(this, App::feedWebPage(d.c_webPage())); + _media = std::make_unique(this, App::feedWebPage(d.c_webPage())); } break; case mtpc_webPageNotModified: LOG(("API Error: webPageNotModified is unexpected in message media.")); break; } @@ -859,12 +770,12 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media) { case mtpc_messageMediaGame: { auto &d = media->c_messageMediaGame().vgame; if (d.type() == mtpc_game) { - _media = std::make_unique(this, App::feedGame(d.c_game())); + _media = std::make_unique(this, App::feedGame(d.c_game())); } } break; case mtpc_messageMediaInvoice: { - _media = std::make_unique(this, media->c_messageMediaInvoice()); - if (static_cast(getMedia())->getReceiptMsgId()) { + _media = std::make_unique(this, media->c_messageMediaInvoice()); + if (_media->invoice()->receiptMsgId) { replaceBuyWithReceiptInMarkup(); } } break; @@ -883,40 +794,10 @@ void HistoryMessage::replaceBuyWithReceiptInMarkup() { } } -void HistoryMessage::initMediaFromDocument(DocumentData *doc, const QString &caption) { - if (doc->sticker()) { - _media = std::make_unique(this, doc); - } else if (doc->isAnimation()) { - _media = std::make_unique(this, doc, caption); - } else if (doc->isVideoFile()) { - _media = std::make_unique(this, doc, caption); - } else { - _media = std::make_unique(this, doc, caption); - } -} - int32 HistoryMessage::plainMaxWidth() const { return st::msgPadding.left() + _text.maxWidth() + st::msgPadding.right(); } -bool HistoryMessage::drawBubble() const { - if (isHiddenByGroup()) { - return false; - } else if (Has()) { - return true; - } - return _media ? (!emptyText() || _media->needsBubble()) : !isEmpty(); -} - -bool HistoryMessage::hasFastReply() const { - return !hasOutLayout() - && (history()->peer->isChat() || history()->peer->isMegagroup()); -} - -bool HistoryMessage::displayFastReply() const { - return hasFastReply() && history()->peer->canWrite(); -} - void HistoryMessage::applyEdition(const MTPDmessage &message) { int keyboardTop = -1; //if (!pendingResize()) {// #TODO edit bot message @@ -941,7 +822,7 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) { } setText(textWithEntities); setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr); - setMedia(message.has_media() ? (&message.vmedia) : nullptr); + initMedia(message.has_media() ? (&message.vmedia) : nullptr); setViewsCount(message.has_views() ? message.vviews.v : -1); finishEdition(keyboardTop); @@ -955,62 +836,23 @@ void HistoryMessage::applyEdition(const MTPDmessageService &message) { void HistoryMessage::applyEditionToEmpty() { setEmptyText(); - setMedia(nullptr); + initMedia(nullptr); setReplyMarkup(nullptr); setViewsCount(-1); finishEditionToEmpty(); } -void HistoryMessage::refreshEditedBadge() { - const auto edited = displayedEditBadge(); - const auto editDate = displayedEditDate(); - const auto dateText = date.toString(cTimeFormat()); - if (edited) { - edited->refresh(dateText, !editDate.isNull()); - } - if (const auto msgsigned = Get()) { - const auto text = (!edited || editDate.isNull()) - ? dateText - : edited->text.originalText(); - msgsigned->refresh(text); - } - initTime(); -} - -bool HistoryMessage::displayForwardedFrom() const { - if (auto forwarded = Get()) { - if (history()->peer->isSelf()) { - return false; - } - return Has() - || !_media - || !_media->isDisplayed() - || !_media->hideForwardedFrom() - || forwarded->originalSender->isChannel(); - } - return false; -} - void HistoryMessage::updateMedia(const MTPMessageMedia *media) { - auto setMediaAllowed = [](HistoryMediaType type) { - return (type == MediaTypeWebPage) - || (type == MediaTypeGame) - || (type == MediaTypeLocation); - }; if (_flags & MTPDmessage_ClientFlag::f_from_inline_bot) { - bool needReSet = true; - if (media && _media) { - needReSet = _media->needReSetInlineResultMedia(*media); - } - if (needReSet) { - setMedia(media); + if (!media || !_media || !_media->updateInlineResultMedia(*media)) { + initMedia(media); } _flags &= ~MTPDmessage_ClientFlag::f_from_inline_bot; - } else if (media && _media && !setMediaAllowed(_media->type())) { - _media->updateSentMedia(*media); } else { - setMedia(media); + if (!media || !_media || !_media->updateSentMedia(*media)) { + initMedia(media); + } } Auth().data().requestItemViewResize(this); } @@ -1033,7 +875,7 @@ void HistoryMessage::eraseFromUnreadMentions() { Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const { auto result = Storage::SharedMediaTypesMask {}; - if (auto media = getMedia()) { + if (const auto media = this->media()) { result.set(media->sharedMediaTypes()); } if (hasTextLinks()) { @@ -1042,89 +884,32 @@ Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const { return result; } -TextWithEntities HistoryMessage::selectedText(TextSelection selection) const { - TextWithEntities logEntryOriginalResult; - const auto textSelection = (selection == FullSelection) - ? AllTextSelection - : IsSubGroupSelection(selection) - ? TextSelection(0, 0) - : selection; - auto textResult = _text.originalTextWithEntities( - textSelection, - ExpandLinksAll); - auto skipped = skipTextSelection(selection); - auto mediaDisplayed = (_media && _media->isDisplayed()); - auto mediaResult = (mediaDisplayed || isHiddenByGroup()) - ? _media->selectedText(skipped) - : TextWithEntities(); - if (auto entry = Get()) { - const auto originalSelection = mediaDisplayed - ? _media->skipSelection(skipped) - : skipped; - logEntryOriginalResult = entry->_page->selectedText(originalSelection); - } - auto result = textResult; - if (result.text.isEmpty()) { - result = std::move(mediaResult); - } else if (!mediaResult.text.isEmpty()) { - result.text += qstr("\n\n"); - TextUtilities::Append(result, std::move(mediaResult)); - } - if (result.text.isEmpty()) { - result = std::move(logEntryOriginalResult); - } else if (!logEntryOriginalResult.text.isEmpty()) { - result.text += qstr("\n\n"); - TextUtilities::Append(result, std::move(logEntryOriginalResult)); - } - if (auto reply = Get()) { - if (selection == FullSelection && reply->replyToMsg) { - TextWithEntities wrapped; - wrapped.text.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.text.size()); - wrapped.text.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n")); - TextUtilities::Append(wrapped, std::move(result)); - result = wrapped; - } - } - if (auto forwarded = Get()) { - if (selection == FullSelection) { - auto fwdinfo = forwarded->text.originalTextWithEntities(AllTextSelection, ExpandLinksAll); - auto wrapped = TextWithEntities(); - wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size()); - wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size()); - wrapped.text.append('['); - TextUtilities::Append(wrapped, std::move(fwdinfo)); - wrapped.text.append(qsl("]\n")); - TextUtilities::Append(wrapped, std::move(result)); - result = wrapped; - } - } - return result; -} +//void HistoryMessage::setMedia(const MTPMessageMedia *media) { -void HistoryMessage::setMedia(const MTPMessageMedia *media) { - if (!_media && (!media || media->type() == mtpc_messageMediaEmpty)) return; + // #TODO grouping and games + //bool mediaRemovedSkipBlock = false; + //if (_media) { + // // Don't update Game media because we loose the consumed text of the message. + // if (_media->type() == MediaTypeGame) { + // return; + // } - bool mediaRemovedSkipBlock = false; - if (_media) { - // Don't update Game media because we loose the consumed text of the message. - if (_media->type() == MediaTypeGame) return; - - mediaRemovedSkipBlock = _media->isDisplayed() && _media->isBubbleBottom(); - _media.reset(); - } - initMedia(media); - auto mediaDisplayed = _media && _media->isDisplayed(); - if (mediaDisplayed && _media->isBubbleBottom() && !mediaRemovedSkipBlock) { - _text.removeSkipBlock(); - _textWidth = -1; - _textHeight = 0; - } else if (mediaRemovedSkipBlock && (!mediaDisplayed || !_media->isBubbleBottom())) { - _text.setSkipBlock(skipBlockWidth(), skipBlockHeight()); - _textWidth = -1; - _textHeight = 0; - } - _history->recountGroupingAround(this); -} + // mediaRemovedSkipBlock = _media->isDisplayed() && _media->isBubbleBottom(); + // _media.reset(); + //} + //initMedia(media); + //auto mediaDisplayed = _media && _media->isDisplayed(); + //if (mediaDisplayed && _media->isBubbleBottom() && !mediaRemovedSkipBlock) { + // _text.removeSkipBlock(); + // _textWidth = -1; + // _textHeight = 0; + //} else if (mediaRemovedSkipBlock && (!mediaDisplayed || !_media->isBubbleBottom())) { + // _text.updateSkipBlock(skipBlockWidth(), skipBlockHeight()); + // _textWidth = -1; + // _textHeight = 0; + //} + //_history->recountGroupingAround(this); +//} void HistoryMessage::setText(const TextWithEntities &textWithEntities) { for_const (auto &entity, textWithEntities.entities) { @@ -1135,25 +920,13 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) { } } - auto mediaDisplayed = _media && _media->isDisplayed(); - if (mediaDisplayed && _media->consumeMessageText(textWithEntities)) { + if (_media && _media->consumeMessageText(textWithEntities)) { setEmptyText(); } else { - auto mediaOnBottom = (_media && _media->isDisplayed() && _media->isBubbleBottom()) || Has(); - if (mediaOnBottom) { - _text.setMarkedText( - st::messageTextStyle, - textWithEntities, - Ui::ItemTextOptions(this)); - } else { - _text.setMarkedText( - st::messageTextStyle, - { - textWithEntities.text + skipBlock(), - textWithEntities.entities - }, - Ui::ItemTextOptions(this)); - } + _text.setMarkedText( + st::messageTextStyle, + textWithEntities, + Ui::ItemTextOptions(this)); _textWidth = -1; _textHeight = 0; } @@ -1222,255 +995,32 @@ bool HistoryMessage::textHasLinks() const { return emptyText() ? false : _text.hasLinks(); } -int HistoryMessage::infoWidth() const { - int result = _timeWidth; - if (auto views = Get()) { - result += st::historyViewsSpace + views->_viewsWidth + st::historyViewsWidth; - } else if (id < 0 && history()->peer->isSelf()) { - if (!hasOutLayout()) { - result += st::historySendStateSpace; - } - } - if (hasOutLayout()) { - result += st::historySendStateSpace; - } - return result; -} - -int HistoryMessage::timeLeft() const { - int result = 0; - if (auto views = Get()) { - result += st::historyViewsSpace + views->_viewsWidth + st::historyViewsWidth; - } else if (id < 0 && history()->peer->isSelf()) { - if (!hasOutLayout()) { - result += st::historySendStateSpace; - } - } - return result; -} - -void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const { - p.setFont(st::msgDateFont); - - bool outbg = hasOutLayout(); - bool invertedsprites = (type == InfoDisplayOverImage || type == InfoDisplayOverBackground); - int32 infoRight = right, infoBottom = bottom; - switch (type) { - case InfoDisplayDefault: - infoRight -= st::msgPadding.right() - st::msgDateDelta.x(); - infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y(); - p.setPen(selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg)); - break; - case InfoDisplayOverImage: - infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x(); - infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y(); - p.setPen(st::msgDateImgFg); - break; - case InfoDisplayOverBackground: - infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x(); - infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y(); - p.setPen(st::msgServiceFg); - break; - } - - int32 infoW = HistoryMessage::infoWidth(); - if (rtl()) infoRight = width - infoRight + infoW; - - int32 dateX = infoRight - infoW; - int32 dateY = infoBottom - st::msgDateFont->height; - if (type == InfoDisplayOverImage) { - int32 dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); - App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); - } else if (type == InfoDisplayOverBackground) { - int32 dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); - App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); - } - dateX += HistoryMessage::timeLeft(); - - if (const auto msgsigned = Get()) { - msgsigned->signature.drawElided(p, dateX, dateY, _timeWidth); - } else if (const auto edited = displayedEditBadge()) { - edited->text.drawElided(p, dateX, dateY, _timeWidth); - } else { - p.drawText(dateX, dateY + st::msgDateFont->ascent, _timeText); - } - - if (auto views = Get()) { - auto icon = ([this, outbg, invertedsprites, selected] { - if (id > 0) { - if (outbg) { - return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsOutSelectedIcon : st::historyViewsOutIcon)); - } - return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsInSelectedIcon : st::historyViewsInIcon)); - } - return &(invertedsprites ? st::historyViewsSendingInvertedIcon : st::historyViewsSendingIcon); - })(); - if (id > 0) { - icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width); - p.drawText(infoRight - infoW + st::historyViewsWidth, infoBottom - st::msgDateFont->descent, views->_viewsText); - } else if (!outbg) { // sending outbg icon will be painted below - auto iconSkip = st::historyViewsSpace + views->_viewsWidth; - icon->paint(p, infoRight - infoW + iconSkip, infoBottom + st::historyViewsTop, width); - } - } else if (id < 0 && history()->peer->isSelf() && !outbg) { - auto icon = &(invertedsprites ? st::historyViewsSendingInvertedIcon : st::historyViewsSendingIcon); - icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width); - } - if (outbg) { - auto icon = ([this, invertedsprites, selected] { - if (id > 0) { - if (unread()) { - return &(invertedsprites ? st::historySentInvertedIcon : (selected ? st::historySentSelectedIcon : st::historySentIcon)); - } - return &(invertedsprites ? st::historyReceivedInvertedIcon : (selected ? st::historyReceivedSelectedIcon : st::historyReceivedIcon)); - } - return &(invertedsprites ? st::historySendingInvertedIcon : st::historySendingIcon); - })(); - icon->paint(p, QPoint(infoRight, infoBottom) + st::historySendStatePosition, width); - } -} - void HistoryMessage::setViewsCount(int32 count) { auto views = Get(); if (!views || views->_views == count || (count >= 0 && views->_views > count)) return; int32 was = views->_viewsWidth; views->_views = count; - views->_viewsText = (views->_views >= 0) ? formatViewsCount(views->_views) : QString(); + views->_viewsText = (views->_views >= 0) ? FormatViewsCount(views->_views) : QString(); views->_viewsWidth = views->_viewsText.isEmpty() ? 0 : st::msgDateFont->width(views->_viewsText); if (was == views->_viewsWidth) { Auth().data().requestItemRepaint(this); } else { - if (_text.hasSkipBlock()) { - _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); - _textWidth = -1; - _textHeight = 0; - } Auth().data().requestItemViewResize(this); } } void HistoryMessage::setRealId(MsgId newId) { HistoryItem::setRealId(newId); - - if (_text.hasSkipBlock()) { - _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); - _textWidth = -1; - _textHeight = 0; - } Auth().data().requestItemViewResize(this); } -void HistoryMessage::drawRightAction(Painter &p, int left, int top, int outerWidth) const { - { - p.setPen(Qt::NoPen); - p.setBrush(st::msgServiceBg); - - PainterHighQualityEnabler hq(p); - p.drawEllipse(rtlrect(left, top, st::historyFastShareSize, st::historyFastShareSize, outerWidth)); - } - if (displayFastShare()) { - st::historyFastShareIcon.paint(p, left, top, outerWidth); - } else { - st::historyGoToOriginalIcon.paint(p, left, top, outerWidth); - } -} - void HistoryMessage::dependencyItemRemoved(HistoryItem *dependency) { if (auto reply = Get()) { reply->itemRemoved(this, dependency); } } -bool HistoryMessage::pointInTime(int right, int bottom, QPoint point, InfoDisplayType type) const { - auto infoRight = right; - auto infoBottom = bottom; - switch (type) { - case InfoDisplayDefault: - infoRight -= st::msgPadding.right() - st::msgDateDelta.x(); - infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y(); - break; - case InfoDisplayOverImage: - infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x(); - infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y(); - break; - } - auto dateX = infoRight - HistoryMessage::infoWidth() + HistoryMessage::timeLeft(); - auto dateY = infoBottom - st::msgDateFont->height; - return QRect(dateX, dateY, HistoryMessage::timeWidth(), st::msgDateFont->height).contains(point); -} - -ClickHandlerPtr HistoryMessage::rightActionLink() const { - if (!_rightActionLink) { - const auto itemId = fullId(); - const auto forwarded = Get(); - const auto savedFromPeer = forwarded ? forwarded->savedFromPeer : nullptr; - const auto savedFromMsgId = forwarded ? forwarded->savedFromMsgId : 0; - _rightActionLink = std::make_shared([=] { - if (auto item = App::histItemById(itemId)) { - if (savedFromPeer && savedFromMsgId) { - App::wnd()->controller()->showPeerHistory( - savedFromPeer, - Window::SectionShow::Way::Forward, - savedFromMsgId); - } else { - FastShareMessage(item); - } - } - }); - } - return _rightActionLink; -} - -ClickHandlerPtr HistoryMessage::fastReplyLink() const { - if (!_fastReplyLink) { - const auto itemId = fullId(); - _fastReplyLink = std::make_shared([=] { - if (const auto item = App::histItemById(itemId)) { - if (const auto main = App::main()) { - main->replyToItem(item); - } - } - }); - } - return _fastReplyLink; -} - -TextSelection HistoryMessage::adjustSelection(TextSelection selection, TextSelectType type) const { - auto result = _text.adjustSelection(selection, type); - auto beforeMediaLength = _text.length(); - if (selection.to <= beforeMediaLength) { - return result; - } - auto mediaDisplayed = _media && _media->isDisplayed(); - if (mediaDisplayed) { - auto mediaSelection = unskipTextSelection(_media->adjustSelection(skipTextSelection(selection), type)); - if (selection.from >= beforeMediaLength) { - result = mediaSelection; - } else { - result.to = mediaSelection.to; - } - } - auto beforeEntryLength = beforeMediaLength + (mediaDisplayed ? _media->fullSelectionLength() : 0); - if (selection.to <= beforeEntryLength) { - return result; - } - if (auto entry = Get()) { - auto entrySelection = mediaDisplayed ? _media->skipSelection(skipTextSelection(selection)) : skipTextSelection(selection); - auto logEntryOriginalSelection = entry->_page->adjustSelection(entrySelection, type); - if (mediaDisplayed) { - logEntryOriginalSelection = _media->unskipSelection(logEntryOriginalSelection); - } - logEntryOriginalSelection = unskipTextSelection(logEntryOriginalSelection); - if (selection.from >= beforeEntryLength) { - result = logEntryOriginalSelection; - } else { - result.to = logEntryOriginalSelection.to; - } - } - return result; -} - QString HistoryMessage::notificationHeader() const { return (!_history->peer->isUser() && !isPost()) ? from()->name : QString(); } diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index f79c895b25..37ee6b9f6f 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -22,6 +22,7 @@ QString GetErrorTextForForward( not_null peer, const HistoryItemsList &items); void FastShareMessage(not_null item); +QString FormatViewsCount(int views); class HistoryMessage : public HistoryItem @@ -142,37 +143,22 @@ public: markup); } - void initTime(); void initMedia(const MTPMessageMedia *media); - void initMediaFromDocument(DocumentData *doc, const QString &caption); int32 plainMaxWidth() const; - bool drawBubble() const; - bool hasBubble() const override { - return drawBubble(); - } - bool hasFastReply() const; - bool displayFastReply() const; - bool displayForwardedFrom() const; + bool allowsForward() const override; + bool allowsEdit(const QDateTime &now) const override; bool uploading() const; - bool displayRightAction() const override; void applyGroupAdminChanges( const base::flat_map &changes) override; - void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const override; - void drawRightAction(Painter &p, int left, int top, int outerWidth) const override; void setViewsCount(int32 count) override; void setRealId(MsgId newId) override; - ClickHandlerPtr rightActionLink() const override; void dependencyItemRemoved(HistoryItem *dependency) override; - bool pointInTime(int right, int bottom, QPoint point, InfoDisplayType type) const override; - - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override; - QString notificationHeader() const override; void applyEdition(const MTPDmessage &message) override; @@ -186,20 +172,10 @@ public: void eraseFromUnreadMentions() override; Storage::SharedMediaTypesMask sharedMediaTypes() const override; - TextWithEntities selectedText(TextSelection selection) const override; void setText(const TextWithEntities &textWithEntities) override; TextWithEntities originalText() const override; bool textHasLinks() const override; - bool displayEditedBadge() const override; - QDateTime displayedEditDate() const override; - - int infoWidth() const override; - int timeLeft() const override; - int timeWidth() const override { - return _timeWidth; - } - int viewsCount() const override; not_null displayFrom() const; bool updateDependencyItem() override; @@ -220,9 +196,6 @@ public: ~HistoryMessage(); -protected: - void refreshEditedBadge() override; - private: HistoryMessage( not_null history, @@ -295,16 +268,9 @@ private: void replaceBuyWithReceiptInMarkup(); void applyEditionToEmpty(); - QDateTime displayedEditDate(bool hasViaBotOrInlineMarkup) const; - const HistoryMessageEdited *displayedEditBadge() const; - HistoryMessageEdited *displayedEditBadge(); - void setMedia(const MTPMessageMedia *media); void setReplyMarkup(const MTPReplyMarkup *markup); - bool displayFastShare() const; - bool displayGoToOriginal() const; - struct CreateConfig; void createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, UserId viaBotId, const QString &postAuthor, const MTPReplyMarkup &markup); void createComponents(const CreateConfig &config); @@ -315,8 +281,6 @@ private: QString _timeText; int _timeWidth = 0; - mutable ClickHandlerPtr _rightActionLink; - mutable ClickHandlerPtr _fastReplyLink; mutable int32 _fromNameVersion = 0; friend class HistoryView::Element; diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index a02e89a091..95e1e80e7b 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_service_message.h" #include "data/data_feed.h" #include "data/data_session.h" +#include "data/data_media_types.h" #include "window/notifications_manager.h" #include "window/window_controller.h" #include "storage/storage_shared_media.h" @@ -204,7 +205,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { case mtpc_messageActionChatEditPhoto: { auto &photo = action.c_messageActionChatEditPhoto().vphoto; if (photo.type() == mtpc_photo) { - _media = std::make_unique(this, history()->peer, photo.c_photo(), st::msgServicePhotoWidth); + _media = std::make_unique( + this, + history()->peer, + App::feedPhoto(photo.c_photo())); } } break; @@ -268,42 +272,12 @@ HistoryService::PreparedText HistoryService::preparePinnedText() { auto result = PreparedText {}; auto pinned = Get(); if (pinned && pinned->msg) { - auto mediaText = ([pinned]() -> QString { - auto media = pinned->msg->getMedia(); - switch (media ? media->type() : MediaTypeCount) { - case MediaTypePhoto: return lang(lng_action_pinned_media_photo); - case MediaTypeVideo: return lang(lng_action_pinned_media_video); - case MediaTypeGrouped: return lang(media->getPhoto() - ? lng_action_pinned_media_photo - : lng_action_pinned_media_video); - case MediaTypeContact: return lang(lng_action_pinned_media_contact); - case MediaTypeFile: return lang(lng_action_pinned_media_file); - case MediaTypeGif: { - if (auto document = media->getDocument()) { - if (document->isVideoMessage()) { - return lang(lng_action_pinned_media_video_message); - } - } - return lang(lng_action_pinned_media_gif); - } break; - case MediaTypeSticker: { - auto emoji = static_cast(media)->emoji(); - if (emoji.isEmpty()) { - return lang(lng_action_pinned_media_sticker); - } - return lng_action_pinned_media_emoji_sticker(lt_emoji, emoji); - } break; - case MediaTypeLocation: return lang(lng_action_pinned_media_location); - case MediaTypeMusicFile: return lang(lng_action_pinned_media_audio); - case MediaTypeVoiceFile: return lang(lng_action_pinned_media_voice); - case MediaTypeGame: { - auto title = static_cast(media)->game()->title; - return lng_action_pinned_media_game(lt_game, title); - } break; + const auto mediaText = [&] { + if (const auto media = pinned->msg->media()) { + return media->pinnedTextSubstring(); } return QString(); - })(); - + }(); result.links.push_back(fromLink()); result.links.push_back(pinned->lnk); if (mediaText.isEmpty()) { @@ -344,8 +318,8 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() { auto computeGameTitle = [gamescore, &result]() -> QString { if (gamescore && gamescore->msg) { - if (const auto media = gamescore->msg->getMedia()) { - if (media->type() == MediaTypeGame) { + if (const auto media = gamescore->msg->media()) { + if (const auto game = media->game()) { const auto row = 0; const auto column = 0; result.links.push_back( @@ -353,7 +327,7 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() { row, column, gamescore->msg->fullId())); - auto titleText = static_cast(media)->game()->title; + auto titleText = game->title; return textcmdLink(result.links.size(), titleText); } } @@ -404,11 +378,11 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() { auto result = PreparedText {}; auto payment = Get(); - auto invoiceTitle = ([payment]() -> QString { + auto invoiceTitle = [&] { if (payment && payment->msg) { - if (auto media = payment->msg->getMedia()) { - if (media->type() == MediaTypeInvoice) { - return static_cast(media)->getTitle(); + if (const auto media = payment->msg->media()) { + if (const auto invoice = media->invoice()) { + return invoice->title; } } return lang(lng_deleted_message); @@ -416,7 +390,7 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() { return lang(lng_contacts_loading); } return QString(); - })(); + }(); if (invoiceTitle.isEmpty()) { result.text = lng_action_payment_done(lt_amount, payment->amount, lt_user, history()->peer->name); @@ -431,16 +405,33 @@ HistoryService::HistoryService(not_null history, const MTPDmessage &me createFromMtp(message); } -HistoryService::HistoryService(not_null history, const MTPDmessageService &message) : - HistoryItem(history, message.vid.v, mtpCastFlags(message.vflags.v), ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) { +HistoryService::HistoryService( + not_null history, + const MTPDmessageService &message) +: HistoryItem( + history, + message.vid.v, + mtpCastFlags(message.vflags.v), + ::date(message.vdate), + message.has_from_id() ? message.vfrom_id.v : 0) { createFromMtp(message); } -HistoryService::HistoryService(not_null history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags, int32 from, PhotoData *photo) : - HistoryItem(history, msgId, flags, date, from) { +HistoryService::HistoryService( + not_null history, + MsgId msgId, + QDateTime date, + const PreparedText &message, + MTPDmessage::Flags flags, + UserId from, + PhotoData *photo) +: HistoryItem(history, msgId, flags, date, from) { setServiceText(message); if (photo) { - _media = std::make_unique(this, history->peer, photo, st::msgServicePhotoWidth); + _media = std::make_unique( + this, + history->peer, + photo); } } @@ -451,10 +442,6 @@ bool HistoryService::updateDependencyItem() { return HistoryItem::updateDependencyItem(); } -TextWithEntities HistoryService::selectedText(TextSelection selection) const { - return _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection); -} - QString HistoryService::inDialogsText(DrawInDialog way) const { return textcmdLink(1, TextUtilities::Clean(notificationText())); } @@ -599,16 +586,14 @@ void HistoryService::applyEdition(const MTPDmessageService &message) { void HistoryService::removeMedia() { if (!_media) return; - bool mediaWasDisplayed = _media->isDisplayed(); _media.reset(); - if (mediaWasDisplayed) { - _textWidth = -1; - _textHeight = 0; - } + _textWidth = -1; + _textHeight = 0; + Auth().data().requestItemViewResize(this); } Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const { - if (auto media = getMedia()) { + if (auto media = this->media()) { return media->sharedMediaTypes(); } return {}; diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index 8923985118..89be2f5e05 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -20,24 +20,24 @@ struct HistoryServiceDependentData { }; struct HistoryServicePinned - : public RuntimeComponent + : public RuntimeComponent , public HistoryServiceDependentData { }; struct HistoryServiceGameScore - : public RuntimeComponent + : public RuntimeComponent , public HistoryServiceDependentData { int score = 0; }; struct HistoryServicePayment - : public RuntimeComponent + : public RuntimeComponent , public HistoryServiceDependentData { QString amount; }; struct HistoryServiceSelfDestruct - : public RuntimeComponent { + : public RuntimeComponent { enum class Type { Photo, Video, @@ -82,12 +82,6 @@ public: return true; } - [[nodiscard]] TextSelection adjustSelection( - TextSelection selection, - TextSelectType type) const override { - return _text.adjustSelection(selection, type); - } - void applyEdition(const MTPDmessageService &message) override; TimeMs getSelfDestructIn(TimeMs now) override; @@ -99,7 +93,6 @@ public: bool serviceMsg() const override { return true; } - TextWithEntities selectedText(TextSelection selection) const override; QString inDialogsText(DrawInDialog way) const override; QString inReplyText() const override; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 7853fa96c3..da4b413357 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "inline_bots/inline_bot_result.h" #include "data/data_drafts.h" #include "data/data_session.h" +#include "data/data_media_types.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_message.h" @@ -648,8 +649,10 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null cont Auth().data().itemLayoutChanged( ) | rpl::start_with_next([this](auto item) { if (_peer && _list) { - if (item->isUnderCursor()) { - _list->onUpdateSelected(); + if (const auto view = item->mainView()) { + if (view->isUnderCursor()) { + _list->onUpdateSelected(); + } } } }, lifetime()); @@ -714,7 +717,10 @@ void HistoryWidget::animatedScrollToItem(MsgId msgId) { return; } - auto scrollTo = snap(itemTopForHighlight(to), 0, _scroll->scrollTopMax()); + auto scrollTo = snap( + itemTopForHighlight(to->mainView()), + 0, + _scroll->scrollTopMax()); animatedScrollToY(scrollTo, to); } @@ -769,9 +775,10 @@ void HistoryWidget::scrollToAnimationCallback(FullMsgId attachToId) { } } -void HistoryWidget::enqueueMessageHighlight(not_null item) { - if (const auto group = item->getFullGroup()) { - item = group->leader; +void HistoryWidget::enqueueMessageHighlight( + not_null view) { + if (const auto group = view->getFullGroup()) { + view = group->leader; } auto enqueueMessageId = [this](MsgId universalId) { if (_highlightQueue.empty() && !_highlightTimer.isActive()) { @@ -782,6 +789,7 @@ void HistoryWidget::enqueueMessageHighlight(not_null item) { checkNextHighlight(); } }; + const auto item = view->data(); if (item->history() == _history) { enqueueMessageId(item->id); } else if (item->history() == _migrated) { @@ -874,12 +882,10 @@ void HistoryWidget::clearHighlightMessages() { stopMessageHighlight(); } -int HistoryWidget::itemTopForHighlight(not_null item) const { - const auto view = item->mainView(); - Assert(view != nullptr); - - if (const auto group = item->getFullGroup()) { - item = group->leader; +int HistoryWidget::itemTopForHighlight( + not_null view) const { + if (const auto group = view->getFullGroup()) { + view = group->leader; } auto itemTop = _list->itemTop(view); Assert(itemTop >= 0); @@ -4354,8 +4360,8 @@ void HistoryWidget::onThumbDocumentUploaded( void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { if (const auto item = App::histItemById(newId)) { - const auto photo = item->getMedia() - ? item->getMedia()->getPhoto() + const auto photo = item->media() + ? item->media()->photo() : nullptr; updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0); Auth().data().requestItemRepaint(item); @@ -4364,8 +4370,8 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (const auto item = App::histItemById(newId)) { - const auto media = item->getMedia(); - const auto document = media ? media->getDocument() : nullptr; + const auto media = item->media(); + const auto document = media ? media->document() : nullptr; const auto sendAction = (document && document->isVoiceMessage()) ? SendAction::Type::UploadVoice : SendAction::Type::UploadFile; @@ -4389,8 +4395,8 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { if (const auto item = App::histItemById(newId)) { - const auto media = item->getMedia(); - const auto document = media ? media->getDocument() : nullptr; + const auto media = item->media(); + const auto document = media ? media->document() : nullptr; const auto sendAction = (document && document->isVoiceMessage()) ? SendAction::Type::UploadVoice : SendAction::Type::UploadFile; @@ -4666,8 +4672,11 @@ int HistoryWidget::countInitialScrollTop() { setMsgId(0); return countInitialScrollTop(); } else { - result = itemTopForHighlight(item); - enqueueMessageHighlight(item); + const auto view = item->mainView(); + Assert(view != nullptr); + + result = itemTopForHighlight(view); + enqueueMessageHighlight(view); } } else if (_history->unreadBar || (_migrated && _migrated->unreadBar)) { result = unreadBarTop(); @@ -5076,7 +5085,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { } } else if (e->key() == Qt::Key_Up) { if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) { - if (_history && _history->lastSentMsg && _history->lastSentMsg->canEdit(::date(unixtime()))) { + if (_history && _history->lastSentMsg && _history->lastSentMsg->allowsEdit(::date(unixtime()))) { if (_field->isEmpty() && !_editMsgId && !_replyToId && _history->lastSentMsg) { editMessage(_history->lastSentMsg); return; @@ -5598,8 +5607,8 @@ void HistoryWidget::editMessage(FullMsgId itemId) { } void HistoryWidget::editMessage(not_null item) { - if (const auto media = item->getMedia()) { - if (media->canEditCaption()) { + if (const auto media = item->media()) { + if (media->allowsEditCaption()) { Ui::show(Box(media, item->fullId())); return; } @@ -5635,9 +5644,9 @@ void HistoryWidget::editMessage(not_null item) { applyDraft(false); _previewData = nullptr; - if (const auto media = item->getMedia()) { - if (media->type() == MediaTypeWebPage) { - _previewData = static_cast(media)->webpage(); + if (const auto media = item->media()) { + if (const auto page = media->webpage()) { + _previewData = page; updatePreview(); } } @@ -6335,8 +6344,8 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { (_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); if (!drawWebPagePreview) { if (drawMsgText) { - if (drawMsgText->getMedia() && drawMsgText->getMedia()->hasReplyPreview()) { - auto replyPreview = drawMsgText->getMedia()->replyPreview(); + if (drawMsgText->media() && drawMsgText->media()->hasReplyPreview()) { + auto replyPreview = drawMsgText->media()->replyPreview(); if (!replyPreview->isNull()) { auto to = QRect(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small)); @@ -6362,7 +6371,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width()); if (!drawWebPagePreview) { const auto firstItem = _toForward.front(); - const auto firstMedia = firstItem->getMedia(); + const auto firstMedia = firstItem->media(); const auto serviceColor = (_toForward.size() > 1) || (firstMedia != nullptr) || firstItem->serviceMsg(); @@ -6498,8 +6507,8 @@ void HistoryWidget::drawPinnedBar(Painter &p) { int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip; if (_pinnedBar->msg) { - if (_pinnedBar->msg->getMedia() && _pinnedBar->msg->getMedia()->hasReplyPreview()) { - ImagePtr replyPreview = _pinnedBar->msg->getMedia()->replyPreview(); + if (_pinnedBar->msg->media() && _pinnedBar->msg->media()->hasReplyPreview()) { + ImagePtr replyPreview = _pinnedBar->msg->media()->replyPreview(); if (!replyPreview->isNull()) { QRect to(left, top, st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small)); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 77dba35aeb..7abaee0653 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -246,7 +246,7 @@ public: bool touchScroll(const QPoint &delta); - void enqueueMessageHighlight(not_null item); + void enqueueMessageHighlight(not_null view); TimeMs highlightStartTime(not_null item) const; MessageIdsList getSelectedItems() const; @@ -673,7 +673,7 @@ private: // Counts scrollTop for placing the scroll right at the unread // messages bar, choosing from _history and _migrated unreadBar. int unreadBarTop() const; - int itemTopForHighlight(not_null item) const; + int itemTopForHighlight(not_null view) const; void scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId); // Scroll to current y without updating the _lastUserScrolled time. diff --git a/Telegram/SourceFiles/history/view/history_view_cursor_state.cpp b/Telegram/SourceFiles/history/view/history_view_cursor_state.cpp index 506edce2a8..86c8710701 100644 --- a/Telegram/SourceFiles/history/view/history_view_cursor_state.cpp +++ b/Telegram/SourceFiles/history/view/history_view_cursor_state.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "history/history_item.h" +#include "history/view/history_view_element.h" HistoryTextState::HistoryTextState(not_null item) : itemId(item->fullId()) { @@ -31,3 +32,21 @@ HistoryTextState::HistoryTextState( : itemId(item->fullId()) , link(link) { } + + +HistoryTextState::HistoryTextState( + not_null view) +: HistoryTextState(view->data()) { +} + +HistoryTextState::HistoryTextState( + not_null view, + const Text::StateResult &state) +: HistoryTextState(view->data(), state) { +} + +HistoryTextState::HistoryTextState( + not_null view, + ClickHandlerPtr link) +: HistoryTextState(view->data(), link) { +} diff --git a/Telegram/SourceFiles/history/view/history_view_cursor_state.h b/Telegram/SourceFiles/history/view/history_view_cursor_state.h index d220d113aa..506e47a1f1 100644 --- a/Telegram/SourceFiles/history/view/history_view_cursor_state.h +++ b/Telegram/SourceFiles/history/view/history_view_cursor_state.h @@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace HistoryView { +class Element; +} // namespace HistoryView + class HistoryItem; enum HistoryCursorState { @@ -25,6 +29,13 @@ struct HistoryTextState { HistoryTextState( not_null item, ClickHandlerPtr link); + HistoryTextState(not_null view); + HistoryTextState( + not_null view, + const Text::StateResult &state); + HistoryTextState( + not_null view, + ClickHandlerPtr link); HistoryTextState( std::nullptr_t, const Text::StateResult &state) @@ -56,7 +67,7 @@ struct HistoryStateRequest { } }; -enum InfoDisplayType { +enum InfoDisplayType : char { InfoDisplayDefault, InfoDisplayOverImage, InfoDisplayOverBackground, diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index f22d3b2b9d..9e7f2540ef 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -8,10 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "history/history_item_components.h" +#include "history/history_item.h" #include "history/history_media.h" +#include "history/history_media_grouped.h" #include "history/history.h" #include "data/data_session.h" +#include "data/data_media_types.h" #include "auth_session.h" +#include "layout.h" namespace HistoryView { namespace { @@ -20,15 +24,51 @@ namespace { constexpr int kAttachMessageToPreviousSecondsDelta = 900; } // namespace + +TextSelection UnshiftItemSelection( + TextSelection selection, + uint16 byLength) { + return (selection == FullSelection) + ? selection + : ::unshiftSelection(selection, byLength); +} + +TextSelection ShiftItemSelection( + TextSelection selection, + uint16 byLength) { + return (selection == FullSelection) + ? selection + : ::shiftSelection(selection, byLength); +} + +TextSelection UnshiftItemSelection( + TextSelection selection, + const Text &byText) { + return UnshiftItemSelection(selection, byText.length()); +} + +TextSelection ShiftItemSelection( + TextSelection selection, + const Text &byText) { + return ShiftItemSelection(selection, byText.length()); +} + Element::Element(not_null data, Context context) : _data(data) +, _media(_data->media() ? _data->media()->createView(this) : nullptr) , _context(context) { + Auth().data().registerItemView(this); + initGroup(); } not_null Element::data() const { return _data; } +HistoryMedia *Element::media() const { + return _media.get(); +} + Context Element::context() const { return _context; } @@ -44,7 +84,7 @@ void Element::setY(int y) { int Element::marginTop() const { const auto item = data(); auto result = 0; - if (!item->isHiddenByGroup()) { + if (!isHiddenByGroup()) { if (isAttachedToPrevious()) { result += st::msgMarginTopAttached; } else { @@ -60,7 +100,11 @@ int Element::marginTop() const { int Element::marginBottom() const { const auto item = data(); - return item->isHiddenByGroup() ? 0 : st::msgMargin.bottom(); + return isHiddenByGroup() ? 0 : st::msgMargin.bottom(); +} + +bool Element::isUnderCursor() const { + return (App::hoveredItem() == this); } void Element::setPendingResize() { @@ -82,6 +126,113 @@ bool Element::isAttachedToNext() const { return _flags & Flag::AttachedToNext; } +int Element::skipBlockWidth() const { + return st::msgDateSpace + infoWidth() - st::msgDateDelta.x(); +} + +int Element::skipBlockHeight() const { + return st::msgDateFont->height - st::msgDateDelta.y(); +} + +QString Element::skipBlock() const { + return textcmdSkipBlock(skipBlockWidth(), skipBlockHeight()); +} + +int Element::infoWidth() const { + return 0; +} + +bool Element::isHiddenByGroup() const { + return _flags & Flag::HiddenByGroup; +} + +void Element::makeGroupMember(not_null leader) { + Expects(leader != this); + + const auto group = Get(); + Assert(group != nullptr); + if (group->leader == this) { + if (auto single = _media ? _media->takeLastFromGroup() : nullptr) { + _media = std::move(single); + } + _flags |= Flag::HiddenByGroup; + Auth().data().requestViewResize(this); + + group->leader = leader; + base::take(group->others); + } else if (group->leader != leader) { + group->leader = leader; + } + + Ensures(isHiddenByGroup()); + Ensures(group->others.empty()); +} + +void Element::makeGroupLeader(std::vector> &&others) { + const auto group = Get(); + Assert(group != nullptr); + + const auto leaderChanged = (group->leader != this); + if (leaderChanged) { + group->leader = this; + _flags &= ~Flag::HiddenByGroup; + Auth().data().requestViewResize(this); + } + group->others = std::move(others); + if (!_media || !_media->applyGroup(group->others)) { + resetGroupMedia(group->others); + data()->invalidateChatsListEntry(); + } + + Ensures(!isHiddenByGroup()); +} + +bool Element::groupIdValidityChanged() { + if (Has()) { + if (_media && _media->canBeGrouped()) { + return false; + } + RemoveComponents(Group::Bit()); + Auth().data().requestViewResize(this); + return true; + } + return false; +} + +void Element::validateGroupId() { + // Just ignore the result. + groupIdValidityChanged(); +} + +Group *Element::getFullGroup() { + if (const auto group = Get()) { + if (group->leader == this) { + return group; + } + return group->leader->Get(); + } + return nullptr; +} + +void Element::initGroup() { + if (const auto groupId = _data->groupId()) { + AddComponents(Group::Bit()); + const auto group = Get(); + group->groupId = groupId; + group->leader = this; + } +} + +void Element::resetGroupMedia( + const std::vector> &others) { + if (!others.empty()) { + _media = std::make_unique(this, others); + } else if (_media) { + _media = _media->takeLastFromGroup(); + } + Auth().data().requestViewResize(this); +} + void Element::previousInBlocksChanged() { recountDisplayDateInBlocks(); recountAttachToPreviousInBlocks(); @@ -92,6 +243,17 @@ void Element::nextInBlocksChanged() { setAttachToNext(false); } +void Element::refreshDataId() { + if (const auto media = this->media()) { + media->refreshParentId(this); + if (const auto group = Get()) { + if (group->leader != this) { + group->leader->refreshDataId(); + } + } + } +} + bool Element::computeIsAttachToPrevious(not_null previous) { const auto item = data(); if (!item->Has() && !item->Has()) { @@ -196,6 +358,53 @@ bool Element::displayFromName() const { return false; } +bool Element::displayForwardedFrom() const { + return false; +} + +bool Element::hasOutLayout() const { + return false; +} + +bool Element::drawBubble() const { + return false; +} + +bool Element::hasBubble() const { + return false; +} + +bool Element::hasFastReply() const { + return false; +} + +bool Element::displayFastReply() const { + return false; +} + +bool Element::displayRightAction() const { + return false; +} + +void Element::drawRightAction( + Painter &p, + int left, + int top, + int outerWidth) const { +} + +ClickHandlerPtr Element::rightActionLink() const { + return ClickHandlerPtr(); +} + +bool Element::displayEditedBadge() const { + return false; +} + +QDateTime Element::displayedEditDate() const { + return QDateTime(); +} + HistoryBlock *Element::block() { return _block; } @@ -261,6 +470,29 @@ Element *Element::nextInBlocks() const { return nullptr; } +void Element::drawInfo( + Painter &p, + int right, + int bottom, + int width, + bool selected, + InfoDisplayType type) const { +} + +bool Element::pointInTime( + int right, + int bottom, + QPoint point, + InfoDisplayType type) const { + return false; +} + +TextSelection Element::adjustSelection( + TextSelection selection, + TextSelectType type) const { + return selection; +} + void Element::clickHandlerActiveChanged( const ClickHandlerPtr &handler, bool active) { @@ -271,7 +503,7 @@ void Element::clickHandlerActiveChanged( } App::hoveredLinkItem(active ? this : nullptr); Auth().data().requestItemRepaint(_data); - if (const auto media = _data->getMedia()) { + if (const auto media = this->media()) { media->clickHandlerActiveChanged(handler, active); } } @@ -286,13 +518,13 @@ void Element::clickHandlerPressedChanged( } App::pressedLinkItem(pressed ? this : nullptr); Auth().data().requestItemRepaint(_data); - if (const auto media = _data->getMedia()) { + if (const auto media = this->media()) { media->clickHandlerPressedChanged(handler, pressed); } } Element::~Element() { - App::messageViewDestroyed(this); + Auth().data().unregisterItemView(this); } } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 7bdaca116b..5862cdb371 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -13,11 +13,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class HistoryBlock; class HistoryItem; +class HistoryMedia; +class HistoryWebPage; struct HistoryTextState; struct HistoryStateRequest; +enum InfoDisplayType : char; namespace HistoryView { +TextSelection UnshiftItemSelection( + TextSelection selection, + uint16 byLength); +TextSelection ShiftItemSelection( + TextSelection selection, + uint16 byLength); +TextSelection UnshiftItemSelection( + TextSelection selection, + const Text &byText); +TextSelection ShiftItemSelection( + TextSelection selection, + const Text &byText); + +class Element; +struct Group : public RuntimeComponent { + MessageGroupId groupId = MessageGroupId::None; + Element *leader = nullptr; + std::vector> others; +}; + enum class Context : char { History, Feed, @@ -26,7 +49,7 @@ enum class Context : char { class Element : public Object -// , public RuntimeComposer + , public RuntimeComposer , public ClickHandlerHost { public: Element(not_null data, Context context); @@ -35,12 +58,15 @@ public: NeedsResize = 0x01, AttachedToPrevious = 0x02, AttachedToNext = 0x04, + HiddenByGroup = 0x08, }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } not_null data() const; + HistoryMedia *media() const; Context context() const; + void refreshDataId(); int y() const; void setY(int y); @@ -49,10 +75,23 @@ public: int marginBottom() const; void setPendingResize(); bool pendingResize() const; + bool isUnderCursor() const; bool isAttachedToPrevious() const; bool isAttachedToNext() const; + int skipBlockWidth() const; + int skipBlockHeight() const; + QString skipBlock() const; + virtual int infoWidth() const; + + bool isHiddenByGroup() const; + void makeGroupMember(not_null leader); + void makeGroupLeader(std::vector> &&others); + bool groupIdValidityChanged(); + void validateGroupId(); + Group *getFullGroup(); + // For blocks context this should be called only from recountAttachToPreviousInBlocks(). void setAttachToPrevious(bool attachToNext); @@ -75,6 +114,23 @@ public: QPoint point, HistoryStateRequest request) const = 0; virtual void updatePressed(QPoint point) = 0; + virtual void drawInfo( + Painter &p, + int right, + int bottom, + int width, + bool selected, + InfoDisplayType type) const; + virtual bool pointInTime( + int right, + int bottom, + QPoint point, + InfoDisplayType type) const; + virtual TextWithEntities selectedText( + TextSelection selection) const = 0; + [[nodiscard]] virtual TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const; // ClickHandlerHost interface. void clickHandlerActiveChanged( @@ -86,10 +142,25 @@ public: // hasFromPhoto() returns true even if we don't display the photo // but we need to skip a place at the left side for this photo - virtual bool displayFromPhoto() const; virtual bool hasFromPhoto() const; + virtual bool displayFromPhoto() const; virtual bool hasFromName() const; virtual bool displayFromName() const; + virtual bool displayForwardedFrom() const; + virtual bool hasOutLayout() const; + virtual bool drawBubble() const; + virtual bool hasBubble() const; + virtual bool hasFastReply() const; + virtual bool displayFastReply() const; + virtual bool displayRightAction() const; + virtual void drawRightAction( + Painter &p, + int left, + int top, + int outerWidth) const; + virtual ClickHandlerPtr rightActionLink() const; + virtual bool displayEditedBadge() const; + virtual QDateTime displayedEditDate() const; // Legacy blocks structure. HistoryBlock *block(); @@ -126,7 +197,12 @@ private: virtual QSize performCountOptimalSize() = 0; virtual QSize performCountCurrentSize(int newWidth) = 0; + void initGroup(); + void resetGroupMedia(const std::vector> &others); + const not_null _data; + std::unique_ptr _media; + int _y = 0; Context _context; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index d8cc07b0d7..abb4b9e55a 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/edit_participant_box.h" #include "data/data_session.h" #include "data/data_feed.h" +#include "data/data_media_types.h" #include "styles/style_history.h" namespace HistoryView { @@ -655,7 +656,7 @@ void ListWidget::paintEvent(QPaintEvent *e) { TextWithEntities ListWidget::getSelectedText() const { return _selectedItem - ? _selectedItem->data()->selectedText(_selectedText) + ? _selectedItem->selectedText(_selectedText) : TextWithEntities(); } @@ -825,7 +826,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto item = view ? view->data().get() : nullptr; const auto itemId = item ? item->fullId() : FullMsgId(); bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg()); - bool canForward = item && item->canForward(); + bool canForward = item && item->allowsForward(); auto msg = dynamic_cast(item); if (isUponSelected > 0) { @@ -833,7 +834,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } else { if (item && !isUponSelected) { auto mediaHasTextForCopy = false; - if (auto media = (msg ? msg->getMedia() : nullptr)) { + if (auto media = view->media()) { mediaHasTextForCopy = media->hasTextForCopy(); if (media->type() == MediaTypeWebPage && static_cast(media)->attach()) { media = static_cast(media)->attach(); @@ -954,8 +955,8 @@ void ListWidget::showContextInFolder(not_null document) { void ListWidget::openContextGif(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (auto media = item->getMedia()) { - if (auto document = media->getDocument()) { + if (auto media = item->media()) { + if (auto document = media->document()) { Messenger::Instance().showDocument(document, item); } } @@ -964,12 +965,9 @@ void ListWidget::openContextGif(FullMsgId itemId) { void ListWidget::copyContextText(FullMsgId itemId) { if (const auto item = App::histItemById(itemId)) { - if (const auto media = item->getMedia()) { - if (media->type() == MediaTypeSticker) { - return; - } + if (const auto view = viewForItem(item)) { + setToClipboard(view->selectedText(FullSelection)); } - setToClipboard(item->selectedText(FullSelection)); } } @@ -1243,7 +1241,7 @@ void ListWidget::updateSelected() { } auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; if (_mouseSelectType != TextSelectType::Letters) { - selection = _mouseActionItem->data()->adjustSelection(selection, _mouseSelectType); + selection = _mouseActionItem->adjustSelection(selection, _mouseSelectType); } if (_selectedText != selection) { _selectedText = selection; @@ -1342,16 +1340,20 @@ void ListWidget::performDrag() { // auto forwardMimeType = QString(); // auto pressedMedia = static_cast(nullptr); // if (auto pressedItem = App::pressedItem()) { - // pressedMedia = pressedItem->getMedia(); - // if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { - // Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem)); + // pressedMedia = pressedItem->media(); + // if (_mouseCursorState == HistoryInDateCursorState + // || (pressedMedia && pressedMedia->dragItem())) { + // Auth().data().setMimeForwardIds( + // Auth().data().itemOrItsGroup(pressedItem->data())); // forwardMimeType = qsl("application/x-td-forward"); // } // } // if (auto pressedLnkItem = App::pressedLinkItem()) { - // if ((pressedMedia = pressedLnkItem->getMedia())) { - // if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { - // Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() }); + // if ((pressedMedia = pressedLnkItem->media())) { + // if (forwardMimeType.isEmpty() + // && pressedMedia->dragItemByHandler(pressedHandler)) { + // Auth().data().setMimeForwardIds( + // { 1, pressedLnkItem->fullId() }); // forwardMimeType = qsl("application/x-td-forward"); // } // } diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index e2c933c284..73000510b3 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "lang/lang_keys.h" #include "mainwidget.h" +#include "mainwindow.h" +#include "window/window_controller.h" #include "auth_session.h" #include "layout.h" #include "styles/style_widgets.h" @@ -177,8 +179,22 @@ style::color FromNameFg(not_null peer, bool selected) { } // namespace +LogEntryOriginal::LogEntryOriginal() = default; + +LogEntryOriginal::LogEntryOriginal(LogEntryOriginal &&other) +: page(std::move(other.page)) { +} + +LogEntryOriginal &LogEntryOriginal::operator=(LogEntryOriginal &&other) { + page = std::move(other.page); + return *this; +} + +LogEntryOriginal::~LogEntryOriginal() = default; + Message::Message(not_null data, Context context) : Element(data, context) { + initLogEntryOriginal(); } not_null Message::message() const { @@ -187,18 +203,27 @@ not_null Message::message() const { QSize Message::performCountOptimalSize() { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto maxWidth = 0; auto minHeight = 0; updateMediaInBubbleState(); - item->refreshEditedBadge(); - if (item->drawBubble()) { + refreshEditedBadge(); + + auto mediaOnBottom = (logEntryOriginal() != nullptr) + || (media && media->isDisplayed() && media->isBubbleBottom()); + if (mediaOnBottom) { + // remove skip + } else { + // add skip + } + + if (drawBubble()) { auto forwarded = item->Get(); auto reply = item->Get(); auto via = item->Get(); - auto entry = item->Get(); + auto entry = logEntryOriginal(); if (forwarded) { forwarded->create(via); } @@ -215,21 +240,19 @@ QSize Message::performCountOptimalSize() { media->initDimensions(); } if (entry) { - entry->_page->initDimensions(); + entry->initDimensions(); } // Entry page is always a bubble bottom. - auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/); - auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->_page->isBubbleTop()); + auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); + auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); if (mediaOnBottom) { - if (item->_text.hasSkipBlock()) { - item->_text.removeSkipBlock(); + if (item->_text.removeSkipBlock()) { item->_textWidth = -1; item->_textHeight = 0; } - } else if (!item->_text.hasSkipBlock()) { - item->_text.setSkipBlock(item->skipBlockWidth(), item->skipBlockHeight()); + } else if (item->_text.updateSkipBlock(skipBlockWidth(), skipBlockHeight())) { item->_textWidth = -1; item->_textHeight = 0; } @@ -259,7 +282,7 @@ QSize Message::performCountOptimalSize() { if (via && !forwarded) { namew += st::msgServiceFont->spacew + via->maxWidth; } - const auto replyWidth = item->hasFastReply() + const auto replyWidth = hasFastReply() ? st::msgFont->width(FastReplyText()) : 0; if (item->hasAdminBadge()) { @@ -289,8 +312,8 @@ QSize Message::performCountOptimalSize() { accumulate_max(maxWidth, replyw); } if (entry) { - accumulate_max(maxWidth, entry->_page->maxWidth()); - minHeight += entry->_page->minHeight(); + accumulate_max(maxWidth, entry->maxWidth()); + minHeight += entry->minHeight(); } } } else if (media) { @@ -323,11 +346,11 @@ void Message::draw( TextSelection selection, TimeMs ms) const { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); - auto outbg = item->hasOutLayout(); - auto bubble = item->drawBubble(); - auto selected = (selection == FullSelection); + const auto outbg = hasOutLayout(); + const auto bubble = drawBubble(); + const auto selected = (selection == FullSelection); auto g = countGeometry(); if (g.width() < 1) { @@ -382,7 +405,7 @@ void Message::draw( fromNameUpdated(g.width()); } - auto entry = item->Get(); + auto entry = logEntryOriginal(); auto mediaDisplayed = media && media->isDisplayed(); auto skipTail = isAttachedToNext() @@ -392,8 +415,8 @@ void Message::draw( PaintBubble(p, g, width(), selected, outbg, displayTail); // Entry page is always a bubble bottom. - auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/); - auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->_page->isBubbleTop()); + auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); + auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); auto trect = g.marginsRemoved(st::msgPadding); if (mediaOnBottom) { @@ -408,9 +431,13 @@ void Message::draw( paintViaBotIdInfo(p, trect, selected); } if (entry) { - trect.setHeight(trect.height() - entry->_page->height()); + trect.setHeight(trect.height() - entry->height()); } - auto needDrawInfo = mediaOnBottom ? !(entry ? entry->_page->customInfoLayout() : media->customInfoLayout()) : true; + auto needDrawInfo = mediaOnBottom + ? !(entry + ? entry->customInfoLayout() + : media->customInfoLayout()) + : true; if (mediaDisplayed) { auto mediaAboveText = media->isAboveMessage(); auto mediaHeight = media->height(); @@ -420,7 +447,7 @@ void Message::draw( paintText(p, trect, selection); } p.translate(mediaLeft, mediaTop); - media->draw(p, clip.translated(-mediaLeft, -mediaTop), item->skipTextSelection(selection), ms); + media->draw(p, clip.translated(-mediaLeft, -mediaTop), skipTextSelection(selection), ms); p.translate(-mediaLeft, -mediaTop); if (mediaAboveText) { @@ -436,28 +463,28 @@ void Message::draw( auto entryLeft = g.left(); auto entryTop = trect.y() + trect.height(); p.translate(entryLeft, entryTop); - auto entrySelection = item->skipTextSelection(selection); + auto entrySelection = skipTextSelection(selection); if (mediaDisplayed) { entrySelection = media->skipSelection(entrySelection); } - entry->_page->draw(p, clip.translated(-entryLeft, -entryTop), entrySelection, ms); + entry->draw(p, clip.translated(-entryLeft, -entryTop), entrySelection, ms); p.translate(-entryLeft, -entryTop); } if (needDrawInfo) { - item->HistoryMessage::drawInfo(p, g.left() + g.width(), g.top() + g.height(), 2 * g.left() + g.width(), selected, InfoDisplayDefault); + drawInfo(p, g.left() + g.width(), g.top() + g.height(), 2 * g.left() + g.width(), selected, InfoDisplayDefault); } - if (item->displayRightAction()) { + if (displayRightAction()) { const auto fastShareSkip = snap( (g.height() - st::historyFastShareSize) / 2, 0, st::historyFastShareBottom); const auto fastShareLeft = g.left() + g.width() + st::historyFastShareLeft; const auto fastShareTop = g.top() + g.height() - fastShareSkip - st::historyFastShareSize; - item->drawRightAction(p, fastShareLeft, fastShareTop, width()); + drawRightAction(p, fastShareLeft, fastShareTop, width()); } } else if (media && media->isDisplayed()) { p.translate(g.topLeft()); - media->draw(p, clip.translated(-g.topLeft()), item->skipTextSelection(selection), ms); + media->draw(p, clip.translated(-g.topLeft()), skipTextSelection(selection), ms); p.translate(-g.topLeft()); } @@ -482,7 +509,7 @@ void Message::paintFromName( return 0; }(); const auto replyWidth = [&] { - if (item->isUnderCursor() && item->displayFastReply()) { + if (isUnderCursor() && displayFastReply()) { return st::msgFont->width(FastReplyText()); } return 0; @@ -508,7 +535,7 @@ void Message::paintFromName( auto forwarded = item->Get(); auto via = item->Get(); if (via && !forwarded && availableWidth > 0) { - auto outbg = item->hasOutLayout(); + const auto outbg = hasOutLayout(); p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); p.drawText(availableLeft, trect.top() + st::msgServiceFont->ascent, via->text); auto skipWidth = via->width + st::msgServiceFont->spacew; @@ -517,7 +544,7 @@ void Message::paintFromName( } if (rightWidth) { p.setPen(selected ? st::msgInDateFgSelected : st::msgInDateFg); - p.setFont(ClickHandler::showAsActive(item->_fastReplyLink) + p.setFont(ClickHandler::showAsActive(_fastReplyLink) ? st::msgFont->underline() : st::msgFont); p.drawText( @@ -530,11 +557,11 @@ void Message::paintFromName( } void Message::paintForwardedInfo(Painter &p, QRect &trect, bool selected) const { - const auto item = message(); - if (item->displayForwardedFrom()) { + if (displayForwardedFrom()) { style::font serviceFont(st::msgServiceFont), serviceName(st::msgServiceNameFont); - auto outbg = item->hasOutLayout(); + const auto item = message(); + const auto outbg = hasOutLayout(); p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); p.setFont(serviceFont); @@ -557,7 +584,7 @@ void Message::paintReplyInfo(Painter &p, QRect &trect, bool selected) const { if (selected) { flags |= HistoryMessageReply::PaintFlag::Selected; } - reply->paint(p, item, trect.x(), trect.y(), trect.width(), flags); + reply->paint(p, this, trect.x(), trect.y(), trect.width(), flags); trect.setY(trect.y() + h); } @@ -567,8 +594,9 @@ void Message::paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const { const auto item = message(); if (!displayFromName() && !item->Has()) { if (auto via = item->Get()) { + const auto outbg = hasOutLayout(); p.setFont(st::msgServiceNameFont); - p.setPen(selected ? (item->hasOutLayout() ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (item->hasOutLayout() ? st::msgOutServiceFg : st::msgInServiceFg)); + p.setPen(selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg)); p.drawTextLeft(trect.left(), trect.top(), width(), via->text); trect.setY(trect.y() + st::msgServiceNameFont->height); } @@ -578,7 +606,7 @@ void Message::paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const { void Message::paintText(Painter &p, QRect &trect, TextSelection selection) const { const auto item = message(); - auto outbg = item->hasOutLayout(); + const auto outbg = hasOutLayout(); auto selected = (selection == FullSelection); p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); p.setFont(st::msgFont); @@ -592,9 +620,9 @@ bool Message::hasPoint(QPoint point) const { } const auto item = message(); - if (item->drawBubble()) { + if (drawBubble()) { return g.contains(point); - } else if (const auto media = item->getMedia()) { + } else if (const auto media = this->media()) { return media->hasPoint(point - g.topLeft()); } else { return false; @@ -629,7 +657,7 @@ HistoryTextState Message::getState( QPoint point, HistoryStateRequest request) const { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto result = HistoryTextState(item); @@ -645,13 +673,13 @@ HistoryTextState Message::getState( g.setHeight(g.height() - st::msgBotKbButton.margin - keyboardHeight); } - if (item->drawBubble()) { - auto entry = item->Get(); + if (drawBubble()) { + auto entry = logEntryOriginal(); auto mediaDisplayed = media && media->isDisplayed(); // Entry page is always a bubble bottom. - auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/); - auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->_page->isBubbleTop()); + auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); + auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); auto trect = g.marginsRemoved(st::msgPadding); if (mediaOnBottom) { @@ -666,19 +694,23 @@ HistoryTextState Message::getState( if (getStateViaBotIdInfo(point, trect, &result)) return result; } if (entry) { - auto entryHeight = entry->_page->height(); + auto entryHeight = entry->height(); trect.setHeight(trect.height() - entryHeight); auto entryLeft = g.left(); auto entryTop = trect.y() + trect.height(); if (point.y() >= entryTop && point.y() < entryTop + entryHeight) { - result = entry->_page->getState( + result = entry->getState( point - QPoint(entryLeft, entryTop), request); result.symbol += item->_text.length() + (mediaDisplayed ? media->fullSelectionLength() : 0); } } - auto needDateCheck = mediaOnBottom ? !(entry ? entry->_page->customInfoLayout() : media->customInfoLayout()) : true; + auto needDateCheck = mediaOnBottom + ? !(entry + ? entry->customInfoLayout() + : media->customInfoLayout()) + : true; if (mediaDisplayed) { auto mediaAboveText = media->isAboveMessage(); auto mediaHeight = media->height(); @@ -700,11 +732,11 @@ HistoryTextState Message::getState( getStateText(point, trect, &result, request); } if (needDateCheck) { - if (item->HistoryMessage::pointInTime(g.left() + g.width(), g.top() + g.height(), point, InfoDisplayDefault)) { + if (pointInTime(g.left() + g.width(), g.top() + g.height(), point, InfoDisplayDefault)) { result.cursor = HistoryInDateCursorState; } } - if (item->displayRightAction()) { + if (displayRightAction()) { const auto fastShareSkip = snap( (g.height() - st::historyFastShareSize) / 2, 0, @@ -717,7 +749,7 @@ HistoryTextState Message::getState( st::historyFastShareSize, st::historyFastShareSize ).contains(point)) { - result.link = item->rightActionLink(); + result.link = rightActionLink(); } } } else if (media && media->isDisplayed()) { @@ -743,7 +775,7 @@ bool Message::getStateFromName( const auto item = message(); if (displayFromName()) { const auto replyWidth = [&] { - if (item->isUnderCursor() && item->displayFastReply()) { + if (isUnderCursor() && displayFastReply()) { return st::msgFont->width(FastReplyText()); } return 0; @@ -753,7 +785,7 @@ bool Message::getStateFromName( && point.x() < trect.left() + trect.width() + st::msgPadding.right() && point.y() >= trect.top() - st::msgPadding.top() && point.y() < trect.top() + st::msgServiceFont->height) { - outResult->link = item->fastReplyLink(); + outResult->link = fastReplyLink(); return true; } if (point.y() >= trect.top() && point.y() < trect.top() + st::msgNameFont->height) { @@ -790,8 +822,8 @@ bool Message::getStateForwardedInfo( QRect &trect, not_null outResult, HistoryStateRequest request) const { - const auto item = message(); - if (item->displayForwardedFrom()) { + if (displayForwardedFrom()) { + const auto item = message(); auto forwarded = item->Get(); auto fwdheight = ((forwarded->text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height; if (point.y() >= trect.top() && point.y() < trect.top() + fwdheight) { @@ -872,7 +904,7 @@ bool Message::getStateText( // Forward to media. void Message::updatePressed(QPoint point) { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); if (!media) return; auto g = countGeometry(); @@ -882,7 +914,7 @@ void Message::updatePressed(QPoint point) { g.setHeight(g.height() - keyboardHeight); } - if (item->drawBubble()) { + if (drawBubble()) { auto mediaDisplayed = media && media->isDisplayed(); auto top = marginTop(); auto trect = g.marginsAdded(-st::msgPadding); @@ -890,7 +922,7 @@ void Message::updatePressed(QPoint point) { trect.setY(trect.y() - st::msgPadding.top()); } else { if (displayFromName()) trect.setTop(trect.top() + st::msgNameFont->height); - if (item->displayForwardedFrom()) { + if (displayForwardedFrom()) { auto forwarded = item->Get(); auto fwdheight = ((forwarded->text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height; trect.setTop(trect.top() + fwdheight); @@ -922,6 +954,276 @@ void Message::updatePressed(QPoint point) { } } +TextWithEntities Message::selectedText(TextSelection selection) const { + const auto item = message(); + const auto media = this->media(); + + TextWithEntities logEntryOriginalResult; + const auto textSelection = (selection == FullSelection) + ? AllTextSelection + : IsSubGroupSelection(selection) + ? TextSelection(0, 0) + : selection; + auto textResult = item->_text.originalTextWithEntities( + textSelection, + ExpandLinksAll); + auto skipped = skipTextSelection(selection); + auto mediaDisplayed = (media && media->isDisplayed()); + auto mediaResult = (mediaDisplayed || isHiddenByGroup()) + ? media->selectedText(skipped) + : TextWithEntities(); + if (auto entry = logEntryOriginal()) { + const auto originalSelection = mediaDisplayed + ? media->skipSelection(skipped) + : skipped; + logEntryOriginalResult = entry->selectedText(originalSelection); + } + auto result = textResult; + if (result.text.isEmpty()) { + result = std::move(mediaResult); + } else if (!mediaResult.text.isEmpty()) { + result.text += qstr("\n\n"); + TextUtilities::Append(result, std::move(mediaResult)); + } + if (result.text.isEmpty()) { + result = std::move(logEntryOriginalResult); + } else if (!logEntryOriginalResult.text.isEmpty()) { + result.text += qstr("\n\n"); + TextUtilities::Append(result, std::move(logEntryOriginalResult)); + } + if (auto reply = item->Get()) { + if (selection == FullSelection && reply->replyToMsg) { + TextWithEntities wrapped; + wrapped.text.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.text.size()); + wrapped.text.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n")); + TextUtilities::Append(wrapped, std::move(result)); + result = wrapped; + } + } + if (auto forwarded = item->Get()) { + if (selection == FullSelection) { + auto fwdinfo = forwarded->text.originalTextWithEntities(AllTextSelection, ExpandLinksAll); + auto wrapped = TextWithEntities(); + wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size()); + wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size()); + wrapped.text.append('['); + TextUtilities::Append(wrapped, std::move(fwdinfo)); + wrapped.text.append(qsl("]\n")); + TextUtilities::Append(wrapped, std::move(result)); + result = wrapped; + } + } + return result; +} + +TextSelection Message::adjustSelection( + TextSelection selection, + TextSelectType type) const { + const auto item = message(); + const auto media = this->media(); + + auto result = item->_text.adjustSelection(selection, type); + auto beforeMediaLength = item->_text.length(); + if (selection.to <= beforeMediaLength) { + return result; + } + auto mediaDisplayed = media && media->isDisplayed(); + if (mediaDisplayed) { + auto mediaSelection = unskipTextSelection( + media->adjustSelection(skipTextSelection(selection), type)); + if (selection.from >= beforeMediaLength) { + result = mediaSelection; + } else { + result.to = mediaSelection.to; + } + } + auto beforeEntryLength = beforeMediaLength + + (mediaDisplayed ? media->fullSelectionLength() : 0); + if (selection.to <= beforeEntryLength) { + return result; + } + if (const auto entry = logEntryOriginal()) { + auto entrySelection = mediaDisplayed + ? media->skipSelection(skipTextSelection(selection)) + : skipTextSelection(selection); + auto logEntryOriginalSelection = entry->adjustSelection(entrySelection, type); + if (mediaDisplayed) { + logEntryOriginalSelection = media->unskipSelection(logEntryOriginalSelection); + } + logEntryOriginalSelection = unskipTextSelection(logEntryOriginalSelection); + if (selection.from >= beforeEntryLength) { + result = logEntryOriginalSelection; + } else { + result.to = logEntryOriginalSelection.to; + } + } + return result; +} + +void Message::drawInfo( + Painter &p, + int right, + int bottom, + int width, + bool selected, + InfoDisplayType type) const { + p.setFont(st::msgDateFont); + + bool outbg = hasOutLayout(); + bool invertedsprites = (type == InfoDisplayOverImage) + || (type == InfoDisplayOverBackground); + int32 infoRight = right, infoBottom = bottom; + switch (type) { + case InfoDisplayDefault: + infoRight -= st::msgPadding.right() - st::msgDateDelta.x(); + infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y(); + p.setPen(selected + ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) + : (outbg ? st::msgOutDateFg : st::msgInDateFg)); + break; + case InfoDisplayOverImage: + infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x(); + infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y(); + p.setPen(st::msgDateImgFg); + break; + case InfoDisplayOverBackground: + infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x(); + infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y(); + p.setPen(st::msgServiceFg); + break; + } + + const auto item = message(); + auto infoW = infoWidth(); + if (rtl()) infoRight = width - infoRight + infoW; + + auto dateX = infoRight - infoW; + auto dateY = infoBottom - st::msgDateFont->height; + if (type == InfoDisplayOverImage) { + auto dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); + App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + } else if (type == InfoDisplayOverBackground) { + auto dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); + App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); + } + dateX += timeLeft(); + + if (const auto msgsigned = item->Get()) { + msgsigned->signature.drawElided(p, dateX, dateY, item->_timeWidth); + } else if (const auto edited = displayedEditBadge()) { + edited->text.drawElided(p, dateX, dateY, item->_timeWidth); + } else { + p.drawText(dateX, dateY + st::msgDateFont->ascent, item->_timeText); + } + + if (auto views = item->Get()) { + auto icon = [&] { + if (item->id > 0) { + if (outbg) { + return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsOutSelectedIcon : st::historyViewsOutIcon)); + } + return &(invertedsprites ? st::historyViewsInvertedIcon : (selected ? st::historyViewsInSelectedIcon : st::historyViewsInIcon)); + } + return &(invertedsprites ? st::historyViewsSendingInvertedIcon : st::historyViewsSendingIcon); + }(); + if (item->id > 0) { + icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width); + p.drawText(infoRight - infoW + st::historyViewsWidth, infoBottom - st::msgDateFont->descent, views->_viewsText); + } else if (!outbg) { // sending outbg icon will be painted below + auto iconSkip = st::historyViewsSpace + views->_viewsWidth; + icon->paint(p, infoRight - infoW + iconSkip, infoBottom + st::historyViewsTop, width); + } + } else if (item->id < 0 && item->history()->peer->isSelf() && !outbg) { + auto icon = &(invertedsprites ? st::historyViewsSendingInvertedIcon : st::historyViewsSendingIcon); + icon->paint(p, infoRight - infoW, infoBottom + st::historyViewsTop, width); + } + if (outbg) { + auto icon = [&] { + if (item->id > 0) { + if (item->unread()) { + return &(invertedsprites ? st::historySentInvertedIcon : (selected ? st::historySentSelectedIcon : st::historySentIcon)); + } + return &(invertedsprites ? st::historyReceivedInvertedIcon : (selected ? st::historyReceivedSelectedIcon : st::historyReceivedIcon)); + } + return &(invertedsprites ? st::historySendingInvertedIcon : st::historySendingIcon); + }(); + icon->paint(p, QPoint(infoRight, infoBottom) + st::historySendStatePosition, width); + } +} + +bool Message::pointInTime( + int right, + int bottom, + QPoint point, + InfoDisplayType type) const { + auto infoRight = right; + auto infoBottom = bottom; + switch (type) { + case InfoDisplayDefault: + infoRight -= st::msgPadding.right() - st::msgDateDelta.x(); + infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y(); + break; + case InfoDisplayOverImage: + infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x(); + infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y(); + break; + } + const auto item = message(); + auto dateX = infoRight - infoWidth() + timeLeft(); + auto dateY = infoBottom - st::msgDateFont->height; + return QRect( + dateX, + dateY, + item->_timeWidth, + st::msgDateFont->height).contains(point); +} + +int Message::infoWidth() const { + const auto item = message(); + auto result = item->_timeWidth; + if (auto views = item->Get()) { + result += st::historyViewsSpace + + views->_viewsWidth + + st::historyViewsWidth; + } else if (item->id < 0 && item->history()->peer->isSelf()) { + if (!hasOutLayout()) { + result += st::historySendStateSpace; + } + } + if (hasOutLayout()) { + result += st::historySendStateSpace; + } + return result; +} + +int Message::timeLeft() const { + const auto item = message(); + auto result = 0; + if (auto views = item->Get()) { + result += st::historyViewsSpace + views->_viewsWidth + st::historyViewsWidth; + } else if (item->id < 0 && item->history()->peer->isSelf()) { + if (!hasOutLayout()) { + result += st::historySendStateSpace; + } + } + return result; +} + +void Message::initLogEntryOriginal() { + if (const auto log = message()->Get()) { + AddComponents(LogEntryOriginal::Bit()); + const auto entry = Get(); + entry->page = std::make_unique(this, log->page); + } +} + +HistoryWebPage *Message::logEntryOriginal() const { + if (const auto entry = Get()) { + return entry->page.get(); + } + return nullptr; +} + bool Message::hasFromName() const { switch (context()) { case Context::AdminLog: @@ -929,7 +1231,7 @@ bool Message::hasFromName() const { return true; case Context::History: { const auto item = message(); - return !item->hasOutLayout() + return !hasOutLayout() && (!item->history()->peer->isUser() || item->history()->peer->isSelf()); } break; @@ -943,19 +1245,158 @@ bool Message::displayFromName() const { return true; } +bool Message::displayForwardedFrom() const { + const auto item = message(); + if (auto forwarded = item->Get()) { + if (item->history()->peer->isSelf()) { + return false; + } + const auto media = this->media(); + return item->Has() + || !media + || !media->isDisplayed() + || !media->hideForwardedFrom() + || forwarded->originalSender->isChannel(); + } + return false; +} + +bool Message::hasOutLayout() const { + const auto item = message(); + if (item->history()->peer->isSelf()) { + return !item->Has(); + } + return item->out() && !item->isPost(); +} + +bool Message::drawBubble() const { + const auto item = message(); + if (isHiddenByGroup()) { + return false; + } else if (logEntryOriginal()) { + return true; + } + const auto media = this->media(); + return media + ? (!item->emptyText() || media->needsBubble()) + : !item->isEmpty(); +} + +bool Message::hasBubble() const { + return drawBubble(); +} + +bool Message::hasFastReply() const { + const auto peer = data()->history()->peer; + return !hasOutLayout() && (peer->isChat() || peer->isMegagroup()); +} + +bool Message::displayFastReply() const { + return hasFastReply() && data()->history()->peer->canWrite(); +} + +bool Message::displayRightAction() const { + return displayFastShare() || displayGoToOriginal(); +} + +bool Message::displayFastShare() const { + const auto item = message(); + const auto peer = item->history()->peer; + if (peer->isChannel()) { + return !peer->isMegagroup(); + } else if (const auto user = peer->asUser()) { + if (user->botInfo && !item->out()) { + if (const auto media = this->media()) { + return media->allowsFastShare(); + } + } + } + return false; +} + +bool Message::displayGoToOriginal() const { + const auto item = message(); + const auto peer = item->history()->peer; + if (peer->isSelf()) { + if (const auto forwarded = item->Get()) { + return forwarded->savedFromPeer && forwarded->savedFromMsgId; + } + } + return false; +} + +void Message::drawRightAction( + Painter &p, + int left, + int top, + int outerWidth) const { + p.setPen(Qt::NoPen); + p.setBrush(st::msgServiceBg); + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(rtlrect( + left, + top, + st::historyFastShareSize, + st::historyFastShareSize, + outerWidth)); + } + if (displayFastShare()) { + st::historyFastShareIcon.paint(p, left, top, outerWidth); + } else { + st::historyGoToOriginalIcon.paint(p, left, top, outerWidth); + } +} + +ClickHandlerPtr Message::rightActionLink() const { + if (!_rightActionLink) { + const auto itemId = data()->fullId(); + const auto forwarded = data()->Get(); + const auto savedFromPeer = forwarded ? forwarded->savedFromPeer : nullptr; + const auto savedFromMsgId = forwarded ? forwarded->savedFromMsgId : 0; + _rightActionLink = std::make_shared([=] { + if (const auto item = App::histItemById(itemId)) { + if (savedFromPeer && savedFromMsgId) { + App::wnd()->controller()->showPeerHistory( + savedFromPeer, + Window::SectionShow::Way::Forward, + savedFromMsgId); + } else { + FastShareMessage(item); + } + } + }); + } + return _rightActionLink; +} + +ClickHandlerPtr Message::fastReplyLink() const { + if (!_fastReplyLink) { + const auto itemId = data()->fullId(); + _fastReplyLink = std::make_shared([=] { + if (const auto item = App::histItemById(itemId)) { + if (const auto main = App::main()) { + main->replyToItem(item); + } + } + }); + } + return _fastReplyLink; +} + void Message::updateMediaInBubbleState() { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto mediaHasSomethingBelow = false; auto mediaHasSomethingAbove = false; auto getMediaHasSomethingAbove = [&] { return displayFromName() - || item->displayForwardedFrom() + || displayForwardedFrom() || item->Has() || item->Has(); }; - auto entry = item->Get(); + auto entry = logEntryOriginal(); if (entry) { mediaHasSomethingBelow = true; mediaHasSomethingAbove = getMediaHasSomethingAbove(); @@ -964,14 +1405,14 @@ void Message::updateMediaInBubbleState() { || (media && media->isDisplayed())) ? MediaInBubbleState::Bottom : MediaInBubbleState::None; - entry->_page->setInBubbleState(entryState); + entry->setInBubbleState(entryState); } if (!media) { return; } media->updateNeedBubbleState(); - if (!item->drawBubble()) { + if (!drawBubble()) { media->setInBubbleState(MediaInBubbleState::None); return; } @@ -1002,7 +1443,7 @@ void Message::updateMediaInBubbleState() { void Message::fromNameUpdated(int width) const { const auto item = message(); - const auto replyWidth = item->hasFastReply() + const auto replyWidth = hasFastReply() ? st::msgFont->width(FastReplyText()) : 0; if (item->hasAdminBadge()) { @@ -1023,17 +1464,25 @@ void Message::fromNameUpdated(int width) const { } } +TextSelection Message::skipTextSelection(TextSelection selection) const { + return HistoryView::UnshiftItemSelection(selection, message()->_text); +} + +TextSelection Message::unskipTextSelection(TextSelection selection) const { + return HistoryView::ShiftItemSelection(selection, message()->_text); +} + QRect Message::countGeometry() const { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto maxwidth = qMin(st::msgMaxWidth, maxWidth()); if (media && media->width() < maxwidth) { maxwidth = qMax(media->width(), qMin(maxwidth, item->plainMaxWidth())); } - const auto outLayout = item->hasOutLayout(); - auto contentLeft = (outLayout && !Adaptive::ChatWide()) + const auto outbg = hasOutLayout(); + auto contentLeft = (outbg && !Adaptive::ChatWide()) ? st::msgMargin.right() : st::msgMargin.left(); if (hasFromPhoto()) { @@ -1043,11 +1492,11 @@ QRect Message::countGeometry() const { } auto contentWidth = width() - st::msgMargin.left() - st::msgMargin.right(); - if (item->history()->peer->isSelf() && !outLayout) { + if (item->history()->peer->isSelf() && !outbg) { contentWidth -= st::msgPhotoSkip; } if (contentWidth > maxwidth) { - if (outLayout && !Adaptive::ChatWide()) { + if (outbg && !Adaptive::ChatWide()) { contentLeft += contentWidth - maxwidth; } contentWidth = maxwidth; @@ -1063,7 +1512,7 @@ QRect Message::countGeometry() const { int Message::resizeContentGetHeight(int newWidth) { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); if (newWidth < st::msgMinWidth) { return height(); @@ -1071,7 +1520,7 @@ int Message::resizeContentGetHeight(int newWidth) { auto newHeight = minHeight(); auto contentWidth = newWidth - (st::msgMargin.left() + st::msgMargin.right()); - if (item->history()->peer->isSelf() && !item->hasOutLayout()) { + if (item->history()->peer->isSelf() && !hasOutLayout()) { contentWidth -= st::msgPhotoSkip; } if (contentWidth < st::msgPadding.left() + st::msgPadding.right() + 1) { @@ -1079,11 +1528,11 @@ int Message::resizeContentGetHeight(int newWidth) { } else if (contentWidth > st::msgMaxWidth) { contentWidth = st::msgMaxWidth; } - if (item->drawBubble()) { + if (drawBubble()) { auto forwarded = item->Get(); auto reply = item->Get(); auto via = item->Get(); - auto entry = item->Get(); + auto entry = logEntryOriginal(); auto mediaDisplayed = false; if (media) { @@ -1091,18 +1540,18 @@ int Message::resizeContentGetHeight(int newWidth) { } // Entry page is always a bubble bottom. - auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/); - auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->_page->isBubbleTop()); + auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); + auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); if (contentWidth >= maxWidth()) { if (mediaDisplayed) { media->resizeGetHeight(maxWidth()); if (entry) { - newHeight += entry->_page->resizeGetHeight(countGeometry().width()); + newHeight += entry->resizeGetHeight(countGeometry().width()); } } else if (entry) { // In case of text-only message it is counted in minHeight already. - entry->_page->resizeGetHeight(countGeometry().width()); + entry->resizeGetHeight(countGeometry().width()); } } else { if (item->emptyText()) { @@ -1127,10 +1576,10 @@ int Message::resizeContentGetHeight(int newWidth) { if (mediaDisplayed) { newHeight += media->resizeGetHeight(contentWidth); if (entry) { - newHeight += entry->_page->resizeGetHeight(countGeometry().width()); + newHeight += entry->resizeGetHeight(countGeometry().width()); } } else if (entry) { - newHeight += entry->_page->resizeGetHeight(contentWidth); + newHeight += entry->resizeGetHeight(contentWidth); } } @@ -1142,7 +1591,7 @@ int Message::resizeContentGetHeight(int newWidth) { newHeight += st::msgNameFont->height; } - if (item->displayForwardedFrom()) { + if (displayForwardedFrom()) { auto fwdheight = ((forwarded->text.maxWidth() > (countGeometry().width() - st::msgPadding.left() - st::msgPadding.right())) ? 2 : 1) * st::semiboldFont->height; newHeight += fwdheight; } @@ -1188,4 +1637,91 @@ QSize Message::performCountCurrentSize(int newWidth) { return { newWidth, newHeight }; } +void Message::refreshEditedBadge() { + const auto item = message(); + const auto edited = displayedEditBadge(); + const auto editDate = displayedEditDate(); + const auto dateText = item->date.toString(cTimeFormat()); + if (edited) { + edited->refresh(dateText, !editDate.isNull()); + } + if (const auto msgsigned = item->Get()) { + const auto text = (!edited || editDate.isNull()) + ? dateText + : edited->text.originalText(); + msgsigned->refresh(text); + } + initTime(); +} + +void Message::initTime() { + const auto item = message(); + if (const auto msgsigned = item->Get()) { + item->_timeWidth = msgsigned->maxWidth(); + } else if (const auto edited = displayedEditBadge()) { + item->_timeWidth = edited->maxWidth(); + } else { + item->_timeText = item->date.toString(cTimeFormat()); + item->_timeWidth = st::msgDateFont->width(item->_timeText); + } + if (const auto views = item->Get()) { + views->_viewsText = (views->_views >= 0) + ? FormatViewsCount(views->_views) + : QString(); + views->_viewsWidth = views->_viewsText.isEmpty() + ? 0 + : st::msgDateFont->width(views->_viewsText); + } + if (item->_text.hasSkipBlock()) { + if (item->_text.updateSkipBlock(skipBlockWidth(), skipBlockHeight())) { + item->_textWidth = -1; + item->_textHeight = 0; + } + } +} + +bool Message::displayEditedBadge() const { + return !displayedEditDate().isNull(); +} + +QDateTime Message::displayedEditDate() const { + const auto item = message(); + auto hasViaBotId = item->Has(); + auto hasInlineMarkup = (item->inlineReplyMarkup() != nullptr); + return displayedEditDate(hasViaBotId || hasInlineMarkup); +} + +QDateTime Message::displayedEditDate( + bool hasViaBotOrInlineMarkup) const { + if (hasViaBotOrInlineMarkup) { + return QDateTime(); + } else if (const auto fromUser = message()->from()->asUser()) { + if (fromUser->botInfo) { + return QDateTime(); + } + } + if (const auto edited = displayedEditBadge()) { + return edited->date; + } + return QDateTime(); +} + +HistoryMessageEdited *Message::displayedEditBadge() { + if (const auto media = this->media()) { + if (media->overrideEditedDate()) { + return media->displayedEditBadge(); + } + } + return message()->Get(); +} + +const HistoryMessageEdited *Message::displayedEditBadge() const { + if (const auto media = this->media()) { + if (media->overrideEditedDate()) { + return media->displayedEditBadge(); + } + } + return message()->Get(); +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 311b11fe38..6e21a2092c 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -10,9 +10,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" class HistoryMessage; +struct HistoryMessageEdited; namespace HistoryView { +// Special type of Component for the channel actions log. +struct LogEntryOriginal + : public RuntimeComponent { + LogEntryOriginal(); + LogEntryOriginal(LogEntryOriginal &&other); + LogEntryOriginal &operator=(LogEntryOriginal &&other); + ~LogEntryOriginal(); + + std::unique_ptr page; + +}; + class Message : public Element { public: Message(not_null data, Context context); @@ -27,20 +40,58 @@ public: QPoint point, HistoryStateRequest request) const override; void updatePressed(QPoint point) override; + void drawInfo( + Painter &p, + int right, + int bottom, + int width, + bool selected, + InfoDisplayType type) const override; + bool pointInTime( + int right, + int bottom, + QPoint point, + InfoDisplayType type) const override; + TextWithEntities selectedText(TextSelection selection) const override; + TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override; // hasFromPhoto() returns true even if we don't display the photo // but we need to skip a place at the left side for this photo - bool displayFromPhoto() const override; bool hasFromPhoto() const override; - + bool displayFromPhoto() const override; bool hasFromName() const override; bool displayFromName() const override; + bool displayForwardedFrom() const override; + bool hasOutLayout() const override; + bool drawBubble() const override; + bool hasBubble() const override; + bool hasFastReply() const override; + bool displayFastReply() const override; + bool displayRightAction() const override; + void drawRightAction( + Painter &p, + int left, + int top, + int outerWidth) const override; + ClickHandlerPtr rightActionLink() const override; + bool displayEditedBadge() const override; + QDateTime displayedEditDate() const override; + int infoWidth() const override; private: not_null message() const; + void initLogEntryOriginal(); + void refreshEditedBadge(); void fromNameUpdated(int width) const; + [[nodiscard]] TextSelection skipTextSelection( + TextSelection selection) const; + [[nodiscard]] TextSelection unskipTextSelection( + TextSelection selection) const; + void paintFromName(Painter &p, QRect &trect, bool selected) const; void paintForwardedInfo(Painter &p, QRect &trect, bool selected) const; void paintReplyInfo(Painter &p, QRect &trect, bool selected) const; @@ -78,6 +129,20 @@ private: QSize performCountOptimalSize() override; QSize performCountCurrentSize(int newWidth) override; + bool displayFastShare() const; + bool displayGoToOriginal() const; + ClickHandlerPtr fastReplyLink() const; + QDateTime displayedEditDate(bool hasViaBotOrInlineMarkup) const; + const HistoryMessageEdited *displayedEditBadge() const; + HistoryMessageEdited *displayedEditBadge(); + void initTime(); + int timeLeft() const; + + HistoryWebPage *logEntryOriginal() const; + + mutable ClickHandlerPtr _rightActionLink; + mutable ClickHandlerPtr _fastReplyLink; + }; } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.cpp b/Telegram/SourceFiles/history/view/history_view_service_message.cpp index d2dce1aa55..9dd7136358 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_service_message.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_abstract_structure.h" #include "styles/style_history.h" #include "mainwidget.h" +#include "layout.h" #include "lang/lang_keys.h" namespace HistoryView { @@ -309,7 +310,7 @@ QRect Service::countGeometry() const { QSize Service::performCountCurrentSize(int newWidth) { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto newHeight = item->displayedDateHeight(); if (auto unreadbar = item->Get()) { @@ -349,7 +350,7 @@ QSize Service::performCountCurrentSize(int newWidth) { QSize Service::performCountOptimalSize() { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto maxWidth = item->_text.maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right(); auto minHeight = item->_text.minHeight(); @@ -409,11 +410,11 @@ void Service::draw( p.setTextPalette(st::serviceTextPalette); - if (auto media = item->getMedia()) { + if (auto media = this->media()) { height -= st::msgServiceMargin.top() + media->height(); auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top(); p.translate(left, top); - media->draw(p, clip.translated(-left, -top), item->skipTextSelection(selection), ms); + media->draw(p, clip.translated(-left, -top), TextSelection(), ms); p.translate(-left, -top); } @@ -435,7 +436,7 @@ void Service::draw( bool Service::hasPoint(QPoint point) const { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto g = countGeometry(); if (g.width() < 1) { @@ -456,7 +457,7 @@ bool Service::hasPoint(QPoint point) const { HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) const { const auto item = message(); - const auto media = item->getMedia(); + const auto media = this->media(); auto result = HistoryTextState(item); @@ -504,4 +505,15 @@ HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) co void Service::updatePressed(QPoint point) { } +TextWithEntities Service::selectedText(TextSelection selection) const { + return message()->_text.originalTextWithEntities( + (selection == FullSelection) ? AllTextSelection : selection); +} + +TextSelection Service::adjustSelection( + TextSelection selection, + TextSelectType type) const { + return message()->_text.adjustSelection(selection, type); +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.h b/Telegram/SourceFiles/history/view/history_view_service_message.h index 8ac773b392..46e862478c 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.h +++ b/Telegram/SourceFiles/history/view/history_view_service_message.h @@ -27,6 +27,10 @@ public: QPoint point, HistoryStateRequest request) const override; void updatePressed(QPoint point) override; + TextWithEntities selectedText(TextSelection selection) const override; + TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override; private: not_null message() const; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index ed5bcd13a9..fb7b715825 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -9,7 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_controller.h" #include "overview/overview_layout.h" -#include "history/history_media_types.h" +#include "data/data_media_types.h" +#include "data/data_photo.h" +#include "data/data_document.h" +#include "data/data_session.h" #include "history/history_item.h" #include "history/history.h" #include "window/themes/window_theme.h" @@ -27,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "boxes/confirm_box.h" #include "core/file_utilities.h" -#include "data/data_session.h" namespace Layout = Overview::Layout; @@ -837,14 +839,14 @@ std::unique_ptr ListWidget::createLayout( return nullptr; } auto getPhoto = [&]() -> PhotoData* { - if (const auto media = item->getMedia()) { - return media->getPhoto(); + if (const auto media = item->media()) { + return media->photo(); } return nullptr; }; auto getFile = [&]() -> DocumentData* { - if (auto media = item->getMedia()) { - return media->getDocument(); + if (auto media = item->media()) { + return media->document(); } return nullptr; }; @@ -878,7 +880,7 @@ std::unique_ptr ListWidget::createLayout( } return nullptr; case Type::Link: - return std::make_unique(item, item->getMedia()); + return std::make_unique(item, item->media()); case Type::RoundFile: return nullptr; } @@ -1313,7 +1315,7 @@ void ListWidget::showContextMenu( })); } else { if (overSelected != SelectionState::NotOverSelectedItems) { - if (item->canForward()) { + if (item->allowsForward()) { _contextMenu->addAction( lang(lng_context_forward_msg), base::lambda_guarded(this, [this, universalId] { @@ -1497,7 +1499,7 @@ bool ListWidget::changeItemSelection( return false; } iterator->second.canDelete = item->canDelete(); - iterator->second.canForward = item->canForward(); + iterator->second.canForward = item->allowsForward(); return true; } return changeExisting(iterator); diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index 95161be29f..82cba4f3e8 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -78,7 +78,9 @@ public: }; -class LayoutItemBase : public RuntimeComposer, public ClickHandlerHost { +class LayoutItemBase + : public RuntimeComposer + , public ClickHandlerHost { public: LayoutItemBase() { } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a0049c4f0d..838ceb89c8 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer_values.h" #include "data/data_drafts.h" #include "data/data_session.h" +#include "data/data_media_types.h" #include "ui/special_buttons.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" @@ -327,8 +328,8 @@ void MainWidget::checkCurrentFloatPlayer() { last->widget->detach(); } if (auto item = App::histItemById(fullId)) { - if (auto media = item->getMedia()) { - if (auto document = media->getDocument()) { + if (auto media = item->media()) { + if (auto document = media->document()) { if (document->isVideoMessage()) { _playerFloats.push_back(std::make_unique(this, item, [this](not_null instance, bool visible) { instance->hiddenByWidget = !visible; @@ -4908,11 +4909,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (existing) { existing->destroy(); } - App::historyUnregItem(local); - Auth().messageIdChanging.notify({ local, newId }, true); local->setRealId(d.vid.v); - App::historyRegItem(local); - Auth().data().requestItemRepaint(local); } } App::historyUnregRandom(d.vrandom_id.v); diff --git a/Telegram/SourceFiles/media/player/media_player_float.cpp b/Telegram/SourceFiles/media/player/media_player_float.cpp index 822d8236cf..1d30bbd67f 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.cpp +++ b/Telegram/SourceFiles/media/player/media_player_float.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include "data/data_document.h" #include "data/data_session.h" +#include "data/data_media_types.h" #include "history/history_media.h" #include "history/history_item.h" #include "media/media_clip_reader.h" @@ -31,10 +32,10 @@ Float::Float( , _item(item) , _toggleCallback(std::move(toggleCallback)) , _draggedCallback(std::move(draggedCallback)) { - auto media = _item->getMedia(); + auto media = _item->media(); Assert(media != nullptr); - auto document = media->getDocument(); + auto document = media->document(); Assert(document != nullptr); Assert(document->isVideoMessage()); @@ -100,9 +101,10 @@ float64 Float::outRatio() const { void Float::mouseReleaseEvent(QMouseEvent *e) { if (_down) { _down = false; - if (auto media = _item ? _item->getMedia() : nullptr) { - media->playInline(); - } + // #TODO float player + //if (auto media = _item ? _item->getMedia() : nullptr) { + // media->playInline(); + //} } if (_drag) { finishDrag(outRatio() < 0.5); @@ -119,9 +121,10 @@ void Float::finishDrag(bool closed) { void Float::mouseDoubleClickEvent(QMouseEvent *e) { if (_item) { // Handle second click. - if (auto media = _item->getMedia()) { - media->playInline(); - } + // #TODO float player + //if (auto media = _item->getMedia()) { + // media->playInline(); + //} Ui::showPeerHistoryAtItem(_item); } } @@ -191,13 +194,14 @@ void Float::paintEvent(QPaintEvent *e) { } Clip::Reader *Float::getReader() const { - if (auto media = _item ? _item->getMedia() : nullptr) { - if (auto reader = media->getClipReader()) { - if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) { - return reader; - } - } - } + // #TODO float player + //if (auto media = _item ? _item->getMedia() : nullptr) { + // if (auto reader = media->getClipReader()) { + // if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) { + // return reader; + // } + // } + //} return nullptr; } diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 2ea7f7daa4..e9cab536db 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_instance.h" #include "history/history.h" #include "history/history_item.h" -#include "history/history_media.h" +#include "data/data_media_types.h" namespace Media { namespace Player { @@ -222,8 +222,8 @@ bool Instance::moveInPlaylist( } const auto newIndex = *data->playlistIndex + delta; if (const auto item = itemByIndex(data, newIndex)) { - if (const auto media = item->getMedia()) { - if (const auto document = media->getDocument()) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { if (autonext) { _switchToNextNotifier.notify({ data->current, @@ -300,11 +300,12 @@ void Instance::play(const AudioMsgId &audioId) { documentLoadProgress(document); } } else if (document->isVideoMessage()) { - if (const auto item = App::histItemById(audioId.contextId())) { - if (const auto media = item->getMedia()) { - media->playInline(); - } - } + // #TODO float player + //if (const auto item = App::histItemById(audioId.contextId())) { + // if (const auto media = item->getMedia()) { + // media->playInline(); + // } + //} } } @@ -429,8 +430,8 @@ void Instance::preloadNext(not_null data) { } const auto nextIndex = *data->playlistIndex + 1; if (const auto item = itemByIndex(data, nextIndex)) { - if (const auto media = item->getMedia()) { - if (const auto document = media->getDocument()) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { const auto isLoaded = document->loaded( DocumentData::FilePathResolveSaveFromDataSilent); if (!isLoaded) { diff --git a/Telegram/SourceFiles/media/player/media_player_panel.cpp b/Telegram/SourceFiles/media/player/media_player_panel.cpp index 97889e5aee..3c1df1f102 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.cpp +++ b/Telegram/SourceFiles/media/player/media_player_panel.cpp @@ -12,8 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/media/info_media_list_widget.h" #include "history/history.h" #include "history/history_item.h" -#include "history/history_media.h" #include "data/data_document.h" +#include "data/data_media_types.h" #include "ui/widgets/shadow.h" #include "ui/widgets/scroll_area.h" #include "mainwindow.h" @@ -258,8 +258,8 @@ void Panel::refreshList() { const auto contextId = current.contextId(); const auto peer = [&]() -> PeerData* { const auto item = contextId ? App::histItemById(contextId) : nullptr; - const auto media = item ? item->getMedia() : nullptr; - const auto document = media ? media->getDocument() : nullptr; + const auto media = item ? item->media() : nullptr; + const auto document = media ? media->document() : nullptr; if (!document || !document->isSharedMediaMusic()) { return nullptr; } diff --git a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp index d5f393f58f..e78cb8de45 100644 --- a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp +++ b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user_photos.h" #include "data/data_photo.h" #include "data/data_document.h" +#include "data/data_media_types.h" #include "history/history.h" #include "history/history_media.h" #include "styles/style_mediaview.h" @@ -473,10 +474,10 @@ auto GroupThumbs::createThumb(Key key) -> std::unique_ptr { return createThumb(key, photo->date ? photo->thumb : ImagePtr()); } else if (const auto msgId = base::get_if(&key)) { if (const auto item = App::histItemById(*msgId)) { - if (const auto media = item->getMedia()) { - if (const auto photo = media->getPhoto()) { + if (const auto media = item->media()) { + if (const auto photo = media->photo()) { return createThumb(key, photo->thumb); - } else if (const auto document = media->getDocument()) { + } else if (const auto document = media->document()) { return createThumb(key, document->thumb); } } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 027413c070..eff81df00e 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_message.h" #include "history/history_media_types.h" +#include "data/data_media_types.h" #include "window/themes/window_theme_preview.h" #include "window/window_peer_menu.h" #include "observer_peer.h" @@ -1236,13 +1237,13 @@ void MediaView::refreshMediaViewer() { void MediaView::refreshCaption(HistoryItem *item) { _caption = Text(); - const auto media = item ? item->getMedia() : nullptr; + const auto media = item ? item->media() : nullptr; if (!media) { return; } - const auto caption = media->getCaption(); - if (caption.text.isEmpty()) { + const auto caption = media->caption(); + if (caption.isEmpty()) { return; } const auto asBot = [&] { @@ -1254,7 +1255,7 @@ void MediaView::refreshCaption(HistoryItem *item) { _caption = Text(st::msgMinWidth); _caption.setMarkedText( st::mediaviewCaptionStyle, - caption, + { caption, EntitiesInText() }, // #TODO caption entities parse Ui::ItemTextOptions(item)); } @@ -2448,10 +2449,10 @@ MediaView::Entity MediaView::entityForSharedMedia(int index) const { MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const { if (const auto item = App::histItemById(itemId)) { - if (const auto media = item->getMedia()) { - if (const auto photo = media->getPhoto()) { + if (const auto media = item->media()) { + if (const auto photo = media->photo()) { return { photo, item }; - } else if (const auto document = media->getDocument()) { + } else if (const auto document = media->document()) { return { document, item }; } } @@ -2474,7 +2475,7 @@ void MediaView::setContext(base::optional_variant< not_null> context) { if (auto item = base::get_if>(&context)) { _msgid = (*item)->fullId(); - _canForwardItem = (*item)->canForward(); + _canForwardItem = (*item)->allowsForward(); _canDeleteItem = (*item)->canDelete(); _history = (*item)->history(); _peer = _history->peer; diff --git a/Telegram/SourceFiles/mtproto/type_utils.h b/Telegram/SourceFiles/mtproto/type_utils.h index b30e04a10f..5aa62efb97 100644 --- a/Telegram/SourceFiles/mtproto/type_utils.h +++ b/Telegram/SourceFiles/mtproto/type_utils.h @@ -68,8 +68,8 @@ enum class MTPDmessage_ClientFlag : uint32 { // message has an admin badge in supergroup f_has_admin_badge = (1U << 20), - // message is not displayed because it is part of a group - f_hidden_by_group = (1U << 19), + //// message is not displayed because it is part of a group + //f_hidden_by_group = (1U << 19), // update this when adding new client side flags MIN_FIELD = (1U << 19), diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 2e916100e9..e87b50f1f0 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" #include "data/data_web_page.h" +#include "data/data_media_types.h" #include "styles/style_overview.h" #include "styles/style_history.h" #include "core/file_utilities.h" @@ -24,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_instance.h" #include "storage/localstorage.h" #include "history/history_item.h" -#include "history/history_media_types.h" #include "history/history_item_components.h" #include "ui/effects/round_checkbox.h" #include "ui/text_options.h" @@ -1165,7 +1165,7 @@ bool Document::updateStatusText() { Link::Link( not_null parent, - HistoryMedia *media) + Data::Media *media) : ItemBase(parent) { AddComponents(Info::Bit()); @@ -1211,9 +1211,10 @@ Link::Link( } } - _page = (media && media->type() == MediaTypeWebPage) - ? static_cast(media)->webpage().get() - : nullptr; + // #TODO webpage + //_page = (media && media->type() == MediaTypeWebPage) + // ? static_cast(media)->webpage().get() + // : nullptr; if (_page) { mainUrl = _page->url; if (_page->document) { diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index fbe29e128b..5631484239 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -20,6 +20,10 @@ namespace style { struct RoundCheckbox; } // namespace style +namespace Data { +class Media; +} // namespace Data + namespace Overview { namespace Layout { @@ -172,7 +176,7 @@ private: }; -struct Info : public RuntimeComponent { +struct Info : public RuntimeComponent { int top = 0; }; @@ -325,7 +329,7 @@ class Link : public ItemBase { public: Link( not_null parent, - HistoryMedia *media); + Data::Media *media); void initDimensions() override; int32 resizeGetHeight(int32 width) override; diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index dddfbbe342..06d2e62e14 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -2698,24 +2698,35 @@ bool Text::hasSkipBlock() const { return _blocks.empty() ? false : _blocks.back()->type() == TextBlockTSkip; } -void Text::setSkipBlock(int32 width, int32 height) { +bool Text::updateSkipBlock(int width, int height) { if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) { - auto block = static_cast(_blocks.back().get()); - if (block->width() == width && block->height() == height) return; + const auto block = static_cast(_blocks.back().get()); + if (block->width() == width && block->height() == height) { + return false; + } _text.resize(block->from()); _blocks.pop_back(); } _text.push_back('_'); - _blocks.push_back(std::make_unique(_st->font, _text, _text.size() - 1, width, height, 0)); + _blocks.push_back(std::make_unique( + _st->font, + _text, + _text.size() - 1, + width, + height, + 0)); recountNaturalSize(false); + return true; } -void Text::removeSkipBlock() { - if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) { - _text.resize(_blocks.back()->from()); - _blocks.pop_back(); - recountNaturalSize(false); +bool Text::removeSkipBlock() { + if (_blocks.empty() || _blocks.back()->type() != TextBlockTSkip) { + return false; } + _text.resize(_blocks.back()->from()); + _blocks.pop_back(); + recountNaturalSize(false); + return true; } int Text::countWidth(int width) const { diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index 01200abdfc..fee4ab42e1 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -89,8 +89,8 @@ public: bool hasLinks() const; bool hasSkipBlock() const; - void setSkipBlock(int32 width, int32 height); - void removeSkipBlock(); + bool updateSkipBlock(int width, int height); + bool removeSkipBlock(); int32 maxWidth() const { return _maxWidth.ceil().toInt(); diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index f1a33a0059..3376fc88c9 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -427,13 +427,24 @@ void Manager::notificationReplied( } void NativeManager::doShowNotification(HistoryItem *item, int forwardedCount) { - auto options = getNotificationOptions(item); + const auto options = getNotificationOptions(item); - QString title = options.hideNameAndPhoto ? qsl("Telegram Desktop") : item->history()->peer->name; - QString subtitle = options.hideNameAndPhoto ? QString() : item->notificationHeader(); - QString text = options.hideMessageText ? lang(lng_notification_preview) : (forwardedCount < 2 ? item->notificationText() : lng_forward_messages(lt_count, forwardedCount)); + const auto title = options.hideNameAndPhoto ? qsl("Telegram Desktop") : item->history()->peer->name; + const auto subtitle = options.hideNameAndPhoto ? QString() : item->notificationHeader(); + const auto text = options.hideMessageText + ? lang(lng_notification_preview) + : (forwardedCount < 2 + ? item->notificationText() + : lng_forward_messages(lt_count, forwardedCount)); - doShowNativeNotification(item->history()->peer, item->id, title, subtitle, text, options.hideNameAndPhoto, options.hideReplyButton); + doShowNativeNotification( + item->history()->peer, + item->id, + title, + subtitle, + text, + options.hideNameAndPhoto, + options.hideReplyButton); } System::~System() = default; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index f16ec59b46..7cdd6e0acd 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -173,6 +173,10 @@ <(src_loc)/data/data_feed_messages.h <(src_loc)/data/data_flags.h <(src_loc)/data/data_game.h +<(src_loc)/data/data_groups.cpp +<(src_loc)/data/data_groups.h +<(src_loc)/data/data_media_types.cpp +<(src_loc)/data/data_media_types.h <(src_loc)/data/data_messages.cpp <(src_loc)/data/data_messages.h <(src_loc)/data/data_notify_settings.cpp