mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Use HistoryMedia as view, add Data::Media.
This commit is contained in:
parent
97a9089ebf
commit
7425e80f05
66 changed files with 4463 additions and 3130 deletions
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "core/tl_help.h"
|
#include "core/tl_help.h"
|
||||||
#include "base/overload.h"
|
#include "base/overload.h"
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
|
@ -2936,13 +2937,13 @@ void ApiWrap::sendUploadedPhoto(
|
||||||
const MTPInputFile &file,
|
const MTPInputFile &file,
|
||||||
bool silent) {
|
bool silent) {
|
||||||
if (const auto item = App::histItemById(localId)) {
|
if (const auto item = App::histItemById(localId)) {
|
||||||
const auto caption = item->getMedia()
|
const auto caption = item->media()
|
||||||
? item->getMedia()->getCaption()
|
? item->media()->caption()
|
||||||
: TextWithEntities();
|
: QString();
|
||||||
const auto media = MTP_inputMediaUploadedPhoto(
|
const auto media = MTP_inputMediaUploadedPhoto(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
file,
|
file,
|
||||||
MTP_string(caption.text),
|
MTP_string(caption),
|
||||||
MTPVector<MTPInputDocument>(),
|
MTPVector<MTPInputDocument>(),
|
||||||
MTP_int(0));
|
MTP_int(0));
|
||||||
if (const auto groupId = item->groupId()) {
|
if (const auto groupId = item->groupId()) {
|
||||||
|
@ -2959,9 +2960,9 @@ void ApiWrap::sendUploadedDocument(
|
||||||
const base::optional<MTPInputFile> &thumb,
|
const base::optional<MTPInputFile> &thumb,
|
||||||
bool silent) {
|
bool silent) {
|
||||||
if (const auto item = App::histItemById(localId)) {
|
if (const auto item = App::histItemById(localId)) {
|
||||||
auto media = item->getMedia();
|
auto media = item->media();
|
||||||
if (auto document = media ? media->getDocument() : nullptr) {
|
if (auto document = media ? media->document() : nullptr) {
|
||||||
const auto caption = media->getCaption();
|
const auto caption = media->caption();
|
||||||
const auto groupId = item->groupId();
|
const auto groupId = item->groupId();
|
||||||
const auto flags = MTPDinputMediaUploadedDocument::Flags(0)
|
const auto flags = MTPDinputMediaUploadedDocument::Flags(0)
|
||||||
| (thumb
|
| (thumb
|
||||||
|
@ -2976,7 +2977,7 @@ void ApiWrap::sendUploadedDocument(
|
||||||
thumb ? *thumb : MTPInputFile(),
|
thumb ? *thumb : MTPInputFile(),
|
||||||
MTP_string(document->mimeString()),
|
MTP_string(document->mimeString()),
|
||||||
ComposeSendingDocumentAttributes(document),
|
ComposeSendingDocumentAttributes(document),
|
||||||
MTP_string(caption.text),
|
MTP_string(caption),
|
||||||
MTPVector<MTPInputDocument>(),
|
MTPVector<MTPInputDocument>(),
|
||||||
MTP_int(0));
|
MTP_int(0));
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
|
@ -3012,10 +3013,10 @@ void ApiWrap::uploadAlbumMedia(
|
||||||
if (!item) {
|
if (!item) {
|
||||||
failed();
|
failed();
|
||||||
}
|
}
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto photo = media->getPhoto()) {
|
if (const auto photo = media->photo()) {
|
||||||
photo->setWaitingForAlbum();
|
photo->setWaitingForAlbum();
|
||||||
} else if (const auto document = media->getDocument()) {
|
} else if (const auto document = media->document()) {
|
||||||
document->setWaitingForAlbum();
|
document->setWaitingForAlbum();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "data/data_abstract_structure.h"
|
#include "data/data_abstract_structure.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_location_manager.h"
|
#include "history/history_location_manager.h"
|
||||||
|
@ -978,10 +979,10 @@ namespace {
|
||||||
|
|
||||||
void checkSavedGif(HistoryItem *item) {
|
void checkSavedGif(HistoryItem *item) {
|
||||||
if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == App::self())) {
|
if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == App::self())) {
|
||||||
if (auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (auto doc = media->getDocument()) {
|
if (const auto document = media->document()) {
|
||||||
if (doc->isGifv()) {
|
if (document->isGifv()) {
|
||||||
addSavedGif(doc);
|
addSavedGif(document);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1836,24 +1837,6 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void messageViewDestroyed(not_null<HistoryView::Element*> 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) {
|
void historyUnregItem(HistoryItem *item) {
|
||||||
auto data = fetchMsgsData(item->channelId(), false);
|
auto data = fetchMsgsData(item->channelId(), false);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
@ -2474,11 +2457,12 @@ namespace {
|
||||||
if (!::gifItems.isEmpty()) {
|
if (!::gifItems.isEmpty()) {
|
||||||
auto gifs = ::gifItems;
|
auto gifs = ::gifItems;
|
||||||
for_const (auto item, gifs) {
|
for_const (auto item, gifs) {
|
||||||
if (auto media = item->getMedia()) {
|
// #TODO GIFs
|
||||||
if (!media->isRoundVideoPlaying()) {
|
//if (auto media = item->getMedia()) {
|
||||||
media->stopInline();
|
// if (!media->isRoundVideoPlaying()) {
|
||||||
}
|
// media->stopInline();
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2486,9 +2470,9 @@ namespace {
|
||||||
QString phoneFromSharedContact(int32 userId) {
|
QString phoneFromSharedContact(int32 userId) {
|
||||||
auto i = ::sharedContactItems.constFind(userId);
|
auto i = ::sharedContactItems.constFind(userId);
|
||||||
if (i != ::sharedContactItems.cend() && !i->empty()) {
|
if (i != ::sharedContactItems.cend() && !i->empty()) {
|
||||||
if (auto media = (*i->cbegin())->getMedia()) {
|
if (const auto media = (*i->cbegin())->media()) {
|
||||||
if (media->type() == MediaTypeContact) {
|
if (const auto contact = media->sharedContact()) {
|
||||||
return static_cast<HistoryContact*>(media)->phone();
|
return contact->phoneNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,7 +237,6 @@ namespace App {
|
||||||
void historyClearItems();
|
void historyClearItems();
|
||||||
void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency);
|
void historyRegDependency(HistoryItem *dependent, HistoryItem *dependency);
|
||||||
void historyUnregDependency(HistoryItem *dependent, HistoryItem *dependency);
|
void historyUnregDependency(HistoryItem *dependent, HistoryItem *dependency);
|
||||||
void messageViewDestroyed(not_null<HistoryView::Element*> view);
|
|
||||||
|
|
||||||
void historyRegRandom(uint64 randomId, const FullMsgId &itemId);
|
void historyRegRandom(uint64 randomId, const FullMsgId &itemId);
|
||||||
void historyUnregRandom(uint64 randomId);
|
void historyUnregRandom(uint64 randomId);
|
||||||
|
|
|
@ -259,4 +259,6 @@ private:
|
||||||
const std::unique_ptr<Window::Notifications::System> _notifications;
|
const std::unique_ptr<Window::Notifications::System> _notifications;
|
||||||
const std::unique_ptr<Core::Changelogs> _changelogs;
|
const std::unique_ptr<Core::Changelogs> _changelogs;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask) {
|
||||||
return i.value();
|
return i.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
const RuntimeComposerMetadata *RuntimeComposer::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
|
const RuntimeComposerMetadata *RuntimeComposerBase::ZeroRuntimeComposerMetadata = GetRuntimeComposerMetadata(0);
|
||||||
|
|
||||||
RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
template <typename Base>
|
||||||
class RuntimeComposer;
|
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(*RuntimeComponentDestruct)(void *location);
|
||||||
typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
|
typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
|
||||||
|
|
||||||
|
@ -38,8 +41,10 @@ struct CeilDivideMinimumOne {
|
||||||
extern RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
extern RuntimeComponentWrapStruct RuntimeComponentWraps[64];
|
||||||
extern QAtomicInt RuntimeComponentIndexLast;
|
extern QAtomicInt RuntimeComponentIndexLast;
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type, typename Base>
|
||||||
struct RuntimeComponent {
|
struct RuntimeComponent {
|
||||||
|
using RuntimeComponentBase = Base;
|
||||||
|
|
||||||
RuntimeComponent() {
|
RuntimeComponent() {
|
||||||
// While there is no std::aligned_alloc().
|
// While there is no std::aligned_alloc().
|
||||||
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
|
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
|
||||||
|
@ -76,7 +81,7 @@ struct RuntimeComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) {
|
static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) {
|
||||||
new (location) Type();
|
new (location) Type();
|
||||||
}
|
}
|
||||||
static void RuntimeComponentDestruct(void *location) {
|
static void RuntimeComponentDestruct(void *location) {
|
||||||
|
@ -134,9 +139,9 @@ private:
|
||||||
|
|
||||||
const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask);
|
const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask);
|
||||||
|
|
||||||
class RuntimeComposer {
|
class RuntimeComposerBase {
|
||||||
public:
|
public:
|
||||||
RuntimeComposer(uint64 mask = 0) : _data(zerodata()) {
|
RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) {
|
||||||
if (mask) {
|
if (mask) {
|
||||||
auto meta = GetRuntimeComposerMetadata(mask);
|
auto meta = GetRuntimeComposerMetadata(mask);
|
||||||
|
|
||||||
|
@ -169,9 +174,9 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RuntimeComposer(const RuntimeComposer &other) = delete;
|
RuntimeComposerBase(const RuntimeComposerBase &other) = delete;
|
||||||
RuntimeComposer &operator=(const RuntimeComposer &other) = delete;
|
RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete;
|
||||||
~RuntimeComposer() {
|
~RuntimeComposerBase() {
|
||||||
if (_data != zerodata()) {
|
if (_data != zerodata()) {
|
||||||
auto meta = _meta();
|
auto meta = _meta();
|
||||||
for (int i = 0; i < meta->last; ++i) {
|
for (int i = 0; i < meta->last; ++i) {
|
||||||
|
@ -184,24 +189,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
bool Has() const {
|
|
||||||
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
Type *Get() {
|
|
||||||
return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()]));
|
|
||||||
}
|
|
||||||
template <typename Type>
|
|
||||||
const Type *Get() const {
|
|
||||||
return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void UpdateComponents(uint64 mask = 0) {
|
void UpdateComponents(uint64 mask = 0) {
|
||||||
if (!_meta()->equals(mask)) {
|
if (!_meta()->equals(mask)) {
|
||||||
RuntimeComposer tmp(mask);
|
RuntimeComposerBase tmp(mask);
|
||||||
tmp.swap(*this);
|
tmp.swap(*this);
|
||||||
if (_data != zerodata() && tmp._data != zerodata()) {
|
if (_data != zerodata() && tmp._data != zerodata()) {
|
||||||
auto meta = _meta(), wasmeta = tmp._meta();
|
auto meta = _meta(), wasmeta = tmp._meta();
|
||||||
|
@ -223,6 +214,9 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template <typename Base>
|
||||||
|
friend class RuntimeComposer;
|
||||||
|
|
||||||
static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata;
|
static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata;
|
||||||
static void *zerodata() {
|
static void *zerodata() {
|
||||||
return &ZeroRuntimeComposerMetadata;
|
return &ZeroRuntimeComposerMetadata;
|
||||||
|
@ -239,8 +233,41 @@ private:
|
||||||
}
|
}
|
||||||
void *_data = nullptr;
|
void *_data = nullptr;
|
||||||
|
|
||||||
void swap(RuntimeComposer &other) {
|
void swap(RuntimeComposerBase &other) {
|
||||||
std::swap(_data, other._data);
|
std::swap(_data, other._data);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Base>
|
||||||
|
class RuntimeComposer : public RuntimeComposerBase {
|
||||||
|
public:
|
||||||
|
using RuntimeComposerBase::RuntimeComposerBase;
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Type,
|
||||||
|
typename = std::enable_if_t<std::is_same_v<
|
||||||
|
typename Type::RuntimeComponentBase,
|
||||||
|
Base>>>
|
||||||
|
bool Has() const {
|
||||||
|
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Type,
|
||||||
|
typename = std::enable_if_t<std::is_same_v<
|
||||||
|
typename Type::RuntimeComponentBase,
|
||||||
|
Base>>>
|
||||||
|
Type *Get() {
|
||||||
|
return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()]));
|
||||||
|
}
|
||||||
|
template <
|
||||||
|
typename Type,
|
||||||
|
typename = std::enable_if_t<std::is_same_v<
|
||||||
|
typename Type::RuntimeComponentBase,
|
||||||
|
Base>>>
|
||||||
|
const Type *Get() const {
|
||||||
|
return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
|
@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/media_clip_reader.h"
|
#include "media/media_clip_reader.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.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 "lang/lang_keys.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
@ -22,59 +24,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
EditCaptionBox::EditCaptionBox(
|
EditCaptionBox::EditCaptionBox(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
not_null<HistoryMedia*> media,
|
not_null<Data::Media*> media,
|
||||||
FullMsgId msgId)
|
FullMsgId msgId)
|
||||||
: _msgId(msgId) {
|
: _msgId(msgId) {
|
||||||
Expects(media->canEditCaption());
|
Expects(media->allowsEditCaption());
|
||||||
|
|
||||||
QSize dimensions;
|
QSize dimensions;
|
||||||
ImagePtr image;
|
ImagePtr image;
|
||||||
QString caption;
|
|
||||||
DocumentData *doc = nullptr;
|
DocumentData *doc = nullptr;
|
||||||
|
|
||||||
switch (media->type()) {
|
if (const auto photo = media->photo()) {
|
||||||
case MediaTypeGif: {
|
|
||||||
_animated = true;
|
|
||||||
doc = static_cast<HistoryGif*>(media.get())->getDocument();
|
|
||||||
dimensions = doc->dimensions;
|
|
||||||
image = doc->thumb;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case MediaTypePhoto: {
|
|
||||||
_photo = true;
|
_photo = true;
|
||||||
auto photo = static_cast<HistoryPhoto*>(media.get())->getPhoto();
|
|
||||||
dimensions = QSize(photo->full->width(), photo->full->height());
|
dimensions = QSize(photo->full->width(), photo->full->height());
|
||||||
image = photo->full;
|
image = photo->full;
|
||||||
} break;
|
} else if (const auto document = media->document()) {
|
||||||
|
dimensions = document->dimensions;
|
||||||
case MediaTypeVideo: {
|
image = document->thumb;
|
||||||
|
if (document->isAnimation()) {
|
||||||
_animated = true;
|
_animated = true;
|
||||||
doc = static_cast<HistoryVideo*>(media.get())->getDocument();
|
} else if (document->isVideoFile()) {
|
||||||
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;
|
|
||||||
_animated = true;
|
_animated = true;
|
||||||
}
|
} else {
|
||||||
} break;
|
|
||||||
|
|
||||||
case MediaTypeFile:
|
|
||||||
case MediaTypeMusicFile:
|
|
||||||
case MediaTypeVoiceFile: {
|
|
||||||
_doc = true;
|
_doc = true;
|
||||||
doc = static_cast<HistoryDocument*>(media.get())->getDocument();
|
|
||||||
image = doc->thumb;
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
caption = media->getCaption().text;
|
doc = document;
|
||||||
|
}
|
||||||
|
auto caption = media->caption();
|
||||||
|
|
||||||
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
|
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
|
||||||
if (image->isNull()) {
|
if (image->isNull()) {
|
||||||
|
|
|
@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
|
|
||||||
class HistoryMedia;
|
namespace Data {
|
||||||
|
class Media;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class InputArea;
|
class InputArea;
|
||||||
|
@ -17,7 +19,7 @@ class InputArea;
|
||||||
|
|
||||||
class EditCaptionBox : public BoxContent, public RPCSender {
|
class EditCaptionBox : public BoxContent, public RPCSender {
|
||||||
public:
|
public:
|
||||||
EditCaptionBox(QWidget*, not_null<HistoryMedia*> media, FullMsgId msgId);
|
EditCaptionBox(QWidget*, not_null<Data::Media*> media, FullMsgId msgId);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
|
@ -15,10 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_media_types.h"
|
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -178,10 +178,11 @@ BoxController::Row::Type BoxController::Row::ComputeType(
|
||||||
not_null<const HistoryItem*> item) {
|
not_null<const HistoryItem*> item) {
|
||||||
if (item->out()) {
|
if (item->out()) {
|
||||||
return Type::Out;
|
return Type::Out;
|
||||||
} else if (auto media = item->getMedia()) {
|
} else if (auto media = item->media()) {
|
||||||
if (media->type() == MediaTypeCall) {
|
if (const auto call = media->call()) {
|
||||||
auto reason = static_cast<HistoryCall*>(media)->reason();
|
const auto reason = call->finishReason;
|
||||||
if (reason == HistoryCall::FinishReason::Busy || reason == HistoryCall::FinishReason::Missed) {
|
if (reason == Data::Call::FinishReason::Busy
|
||||||
|
|| reason == Data::Call::FinishReason::Missed) {
|
||||||
return Type::Missed;
|
return Type::Missed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,15 +285,15 @@ void DocumentOpenClickHandler::doOpen(
|
||||||
if (App::main()) App::main()->mediaMarkRead(data);
|
if (App::main()) App::main()->mediaMarkRead(data);
|
||||||
} else if (data->size < App::kImageSizeLimit) {
|
} else if (data->size < App::kImageSizeLimit) {
|
||||||
if (!data->data().isEmpty() && playAnimation) {
|
if (!data->data().isEmpty() && playAnimation) {
|
||||||
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
|
if (action == ActionOnLoadPlayInline && context) {
|
||||||
context->getMedia()->playInline();
|
Auth().data().requestItemPlayInline(context);
|
||||||
} else {
|
} else {
|
||||||
Messenger::Instance().showDocument(data, context);
|
Messenger::Instance().showDocument(data, context);
|
||||||
}
|
}
|
||||||
} else if (location.accessEnable()) {
|
} else if (location.accessEnable()) {
|
||||||
if (data->isAnimation() || QImageReader(location.name()).canRead()) {
|
if (data->isAnimation() || QImageReader(location.name()).canRead()) {
|
||||||
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
|
if (action == ActionOnLoadPlayInline && context) {
|
||||||
context->getMedia()->playInline();
|
Auth().data().requestItemPlayInline(context);
|
||||||
} else {
|
} else {
|
||||||
Messenger::Instance().showDocument(data, context);
|
Messenger::Instance().showDocument(data, context);
|
||||||
}
|
}
|
||||||
|
@ -548,7 +548,10 @@ void DocumentData::performActionOnLoad() {
|
||||||
auto showImage = !isVideoFile() && (size < App::kImageSizeLimit);
|
auto showImage = !isVideoFile() && (size < App::kImageSizeLimit);
|
||||||
auto playVoice = isVoiceMessage() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
|
auto playVoice = isVoiceMessage() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
|
||||||
auto playMusic = isAudioFile() && (_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 (auto applyTheme = isTheme()) {
|
||||||
if (!loc.isEmpty() && loc.accessEnable()) {
|
if (!loc.isEmpty() && loc.accessEnable()) {
|
||||||
Messenger::Instance().showDocument(this, item);
|
Messenger::Instance().showDocument(this, item);
|
||||||
|
@ -588,8 +591,8 @@ void DocumentData::performActionOnLoad() {
|
||||||
}
|
}
|
||||||
} else if (playAnimation) {
|
} else if (playAnimation) {
|
||||||
if (loaded()) {
|
if (loaded()) {
|
||||||
if (_actionOnLoad == ActionOnLoadPlayInline && item->getMedia()) {
|
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
|
||||||
item->getMedia()->playInline();
|
Auth().data().requestItemPlayInline(item);
|
||||||
} else {
|
} else {
|
||||||
Messenger::Instance().showDocument(this, item);
|
Messenger::Instance().showDocument(this, item);
|
||||||
}
|
}
|
||||||
|
@ -607,8 +610,8 @@ void DocumentData::performActionOnLoad() {
|
||||||
if (App::main()) App::main()->mediaMarkRead(this);
|
if (App::main()) App::main()->mediaMarkRead(this);
|
||||||
} else if (loc.accessEnable()) {
|
} else if (loc.accessEnable()) {
|
||||||
if (showImage && QImageReader(loc.name()).canRead()) {
|
if (showImage && QImageReader(loc.name()).canRead()) {
|
||||||
if (_actionOnLoad == ActionOnLoadPlayInline && item && item->getMedia()) {
|
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
|
||||||
item->getMedia()->playInline();
|
Auth().data().requestItemPlayInline(item);
|
||||||
} else {
|
} else {
|
||||||
Messenger::Instance().showDocument(this, item);
|
Messenger::Instance().showDocument(this, item);
|
||||||
}
|
}
|
||||||
|
@ -764,7 +767,7 @@ void DocumentData::cancel() {
|
||||||
void DocumentData::notifyLayoutChanged() const {
|
void DocumentData::notifyLayoutChanged() const {
|
||||||
auto &items = App::documentItems();
|
auto &items = App::documentItems();
|
||||||
for (auto item : items.value(const_cast<DocumentData*>(this))) {
|
for (auto item : items.value(const_cast<DocumentData*>(this))) {
|
||||||
Auth().data().markItemLayoutChanged(item);
|
Auth().data().markItemLayoutChange(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto items = InlineBots::Layout::documentItems()) {
|
if (auto items = InlineBots::Layout::documentItems()) {
|
||||||
|
|
49
Telegram/SourceFiles/data/data_groups.cpp
Normal file
49
Telegram/SourceFiles/data/data_groups.cpp
Normal file
|
@ -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<HistoryItem*> 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<HistoryItem*> 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<HistoryItem*> 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
|
32
Telegram/SourceFiles/data/data_groups.h
Normal file
32
Telegram/SourceFiles/data/data_groups.h
Normal file
|
@ -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<HistoryItem*> item);
|
||||||
|
void unregisterMessage(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
|
const Group *find(not_null<HistoryItem*> item) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<MessageGroupId, Group> _data;
|
||||||
|
std::map<MessageGroupId, MessageGroupId> _alias;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Data
|
932
Telegram/SourceFiles/data/data_media_types.cpp
Normal file
932
Telegram/SourceFiles/data/data_media_types.cpp
Normal file
|
@ -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<PhotoId>();
|
||||||
|
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<HistoryItem*> parent) : _parent(parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<HistoryItem*> 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<ChannelData*> channel) const {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Media::consumeMessageText(const TextWithEntities &text) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaPhoto::MediaPhoto(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<PhotoData*> photo,
|
||||||
|
const QString &caption)
|
||||||
|
: Media(parent)
|
||||||
|
, _photo(photo)
|
||||||
|
, _caption(caption) {
|
||||||
|
App::regPhotoItem(_photo, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaPhoto::MediaPhoto(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<PeerData*> chat,
|
||||||
|
not_null<PhotoData*> photo)
|
||||||
|
: Media(parent)
|
||||||
|
, _photo(photo)
|
||||||
|
, _chat(chat) {
|
||||||
|
App::regPhotoItem(_photo, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaPhoto::~MediaPhoto() {
|
||||||
|
App::unregPhotoItem(_photo, parent());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Media> MediaPhoto::clone(not_null<HistoryItem*> parent) {
|
||||||
|
return _chat
|
||||||
|
? std::make_unique<MediaPhoto>(parent, _chat, _photo)
|
||||||
|
: std::make_unique<MediaPhoto>(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<ChannelData*> 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<HistoryMedia> MediaPhoto::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
if (_chat) {
|
||||||
|
return std::make_unique<HistoryPhoto>(
|
||||||
|
message,
|
||||||
|
_chat,
|
||||||
|
_photo,
|
||||||
|
st::msgServicePhotoWidth);
|
||||||
|
}
|
||||||
|
return std::make_unique<HistoryPhoto>(message, _photo, _caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaFile::MediaFile(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<DocumentData*> 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<Media> MediaFile::clone(not_null<HistoryItem*> parent) {
|
||||||
|
return std::make_unique<MediaFile>(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<ChannelData*> 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<HistoryMedia> MediaFile::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
if (_document->sticker()) {
|
||||||
|
return std::make_unique<HistorySticker>(message, _document);
|
||||||
|
} else if (_document->isAnimation()) {
|
||||||
|
return std::make_unique<HistoryGif>(message, _document, _caption);
|
||||||
|
} else if (_document->isVideoFile()) {
|
||||||
|
return std::make_unique<HistoryVideo>(message, _document, _caption);
|
||||||
|
}
|
||||||
|
return std::make_unique<HistoryDocument>(message, _document, _caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaContact::MediaContact(
|
||||||
|
not_null<HistoryItem*> 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<Media> MediaContact::clone(not_null<HistoryItem*> parent) {
|
||||||
|
return std::make_unique<MediaContact>(
|
||||||
|
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<HistoryMedia> MediaContact::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
return std::make_unique<HistoryContact>(
|
||||||
|
message,
|
||||||
|
_contact.userId,
|
||||||
|
_contact.firstName,
|
||||||
|
_contact.lastName,
|
||||||
|
_contact.phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaLocation::MediaLocation(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const LocationCoords &coords)
|
||||||
|
: MediaLocation(parent, coords, QString(), QString()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaLocation::MediaLocation(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const LocationCoords &coords,
|
||||||
|
const QString &title,
|
||||||
|
const QString &description)
|
||||||
|
: Media(parent)
|
||||||
|
, _location(App::location(coords))
|
||||||
|
, _title(title)
|
||||||
|
, _description(description) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Media> MediaLocation::clone(not_null<HistoryItem*> parent) {
|
||||||
|
return std::make_unique<MediaLocation>(
|
||||||
|
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<HistoryMedia> MediaLocation::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
return std::make_unique<HistoryLocation>(
|
||||||
|
message,
|
||||||
|
_location,
|
||||||
|
_title,
|
||||||
|
_description);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaCall::MediaCall(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const MTPDmessageActionPhoneCall &call)
|
||||||
|
: Media(parent)
|
||||||
|
, _call(ComputeCallData(call)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Media> MediaCall::clone(not_null<HistoryItem*> 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<HistoryMedia> MediaCall::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
return std::make_unique<HistoryCall>(message, &_call);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MediaCall::Text(
|
||||||
|
not_null<HistoryItem*> 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<HistoryItem*> parent,
|
||||||
|
not_null<WebPageData*> page)
|
||||||
|
: Media(parent)
|
||||||
|
, _page(page) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Media> MediaWebPage::clone(not_null<HistoryItem*> parent) {
|
||||||
|
return std::make_unique<MediaWebPage>(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<HistoryMedia> MediaWebPage::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
return std::make_unique<HistoryWebPage>(message, _page);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaGame::MediaGame(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<GameData*> game)
|
||||||
|
: Media(parent)
|
||||||
|
, _game(game) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Media> MediaGame::clone(not_null<HistoryItem*> parent) {
|
||||||
|
return std::make_unique<MediaGame>(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<ChannelData*> 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<HistoryMedia> MediaGame::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
return std::make_unique<HistoryGame>(message, _game, _consumedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaInvoice::MediaInvoice(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const MTPDmessageMediaInvoice &data)
|
||||||
|
: Media(parent)
|
||||||
|
, _invoice(ComputeInvoiceData(data)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaInvoice::MediaInvoice(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const Invoice &data)
|
||||||
|
: Media(parent)
|
||||||
|
, _invoice(data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Media> MediaInvoice::clone(not_null<HistoryItem*> parent) {
|
||||||
|
return std::make_unique<MediaInvoice>(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<HistoryMedia> MediaInvoice::createView(
|
||||||
|
not_null<HistoryView::Element*> message) {
|
||||||
|
return std::make_unique<HistoryInvoice>(message, &_invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Data
|
347
Telegram/SourceFiles/data/data_media_types.h
Normal file
347
Telegram/SourceFiles/data/data_media_types.h
Normal file
|
@ -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 <typename Enum>
|
||||||
|
class enum_mask;
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
enum class SharedMediaType : char;
|
||||||
|
using SharedMediaTypesMask = base::enum_mask<SharedMediaType>;
|
||||||
|
} // 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<HistoryItem*> parent);
|
||||||
|
virtual ~Media() = default;
|
||||||
|
|
||||||
|
not_null<HistoryItem*> parent() const;
|
||||||
|
|
||||||
|
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<ChannelData*> 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<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<HistoryItem*> _parent;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaPhoto : public Media {
|
||||||
|
public:
|
||||||
|
MediaPhoto(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<PhotoData*> photo,
|
||||||
|
const QString &caption);
|
||||||
|
MediaPhoto(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<PeerData*> chat,
|
||||||
|
not_null<PhotoData*> photo);
|
||||||
|
~MediaPhoto();
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<ChannelData*> channel) const override;
|
||||||
|
|
||||||
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
std::unique_ptr<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
not_null<PhotoData*> _photo;
|
||||||
|
PeerData *_chat = nullptr;
|
||||||
|
QString _caption;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaFile : public Media {
|
||||||
|
public:
|
||||||
|
MediaFile(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
const QString &caption);
|
||||||
|
~MediaFile();
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<ChannelData*> channel) const override;
|
||||||
|
|
||||||
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
std::unique_ptr<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
not_null<DocumentData*> _document;
|
||||||
|
QString _caption;
|
||||||
|
QString _emoji;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaContact : public Media {
|
||||||
|
public:
|
||||||
|
MediaContact(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
UserId userId,
|
||||||
|
const QString &firstName,
|
||||||
|
const QString &lastName,
|
||||||
|
const QString &phoneNumber);
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SharedContact _contact;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaLocation : public Media {
|
||||||
|
public:
|
||||||
|
MediaLocation(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const LocationCoords &coords);
|
||||||
|
MediaLocation(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const LocationCoords &coords,
|
||||||
|
const QString &title,
|
||||||
|
const QString &description);
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
not_null<LocationData*> _location;
|
||||||
|
QString _title;
|
||||||
|
QString _description;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaCall : public Media {
|
||||||
|
public:
|
||||||
|
MediaCall(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const MTPDmessageActionPhoneCall &call);
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
static QString Text(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
CallFinishReason reason);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Call _call;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaWebPage : public Media {
|
||||||
|
public:
|
||||||
|
MediaWebPage(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<WebPageData*> page);
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
not_null<WebPageData*> _page;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaGame : public Media {
|
||||||
|
public:
|
||||||
|
MediaGame(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
not_null<GameData*> game);
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||||
|
|
||||||
|
GameData *game() const override;
|
||||||
|
|
||||||
|
QString notificationText() const override;
|
||||||
|
QString pinnedTextSubstring() const override;
|
||||||
|
QString errorTextForForward(
|
||||||
|
not_null<ChannelData*> channel) const override;
|
||||||
|
|
||||||
|
bool consumeMessageText(const TextWithEntities &text) override;
|
||||||
|
|
||||||
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
std::unique_ptr<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
not_null<GameData*> _game;
|
||||||
|
TextWithEntities _consumedText;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaInvoice : public Media {
|
||||||
|
public:
|
||||||
|
MediaInvoice(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const MTPDmessageMediaInvoice &data);
|
||||||
|
MediaInvoice(
|
||||||
|
not_null<HistoryItem*> parent,
|
||||||
|
const Invoice &data);
|
||||||
|
|
||||||
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> 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<HistoryMedia> createView(
|
||||||
|
not_null<HistoryView::Element*> message) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Invoice _invoice;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -66,7 +66,7 @@ void PhotoData::notifyLayoutChanged() const {
|
||||||
auto i = items.constFind(const_cast<PhotoData*>(this));
|
auto i = items.constFind(const_cast<PhotoData*>(this));
|
||||||
if (i != items.cend()) {
|
if (i != items.cend()) {
|
||||||
for_const (auto item, i.value()) {
|
for_const (auto item, i.value()) {
|
||||||
Auth().data().markItemLayoutChanged(item);
|
Auth().data().markItemLayoutChange(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
|
using ViewElement = HistoryView::Element;
|
||||||
|
|
||||||
Session::Session() {
|
Session::Session() {
|
||||||
Notify::PeerUpdateViewer(
|
Notify::PeerUpdateViewer(
|
||||||
Notify::PeerUpdate::Flag::UserIsContact
|
Notify::PeerUpdate::Flag::UserIsContact
|
||||||
|
@ -27,12 +30,61 @@ Session::Session() {
|
||||||
|
|
||||||
Session::~Session() = default;
|
Session::~Session() = default;
|
||||||
|
|
||||||
void Session::markItemLayoutChanged(not_null<const HistoryItem*> item) {
|
void Session::registerItemView(not_null<ViewElement*> view) {
|
||||||
_itemLayoutChanged.fire_copy(item);
|
_views[view->data()].push_back(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::unregisterItemView(not_null<ViewElement*> 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<const HistoryItem*> item) {
|
||||||
|
_itemLayoutChanges.fire_copy(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<not_null<const HistoryItem*>> Session::itemLayoutChanged() const {
|
rpl::producer<not_null<const HistoryItem*>> Session::itemLayoutChanged() const {
|
||||||
return _itemLayoutChanged.events();
|
return _itemLayoutChanges.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::markViewLayoutChange(not_null<const HistoryView::Element*> view) {
|
||||||
|
_viewLayoutChanges.fire_copy(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<const HistoryView::Element*>> Session::viewLayoutChanged() const {
|
||||||
|
return _viewLayoutChanges.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::markItemIdChange(IdChange event) {
|
||||||
|
_itemIdChanges.fire_copy(event);
|
||||||
|
enumerateItemViews(event.item, [](not_null<HistoryView::Element*> view) {
|
||||||
|
view->refreshDataId();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<Session::IdChange> Session::itemIdChanged() const {
|
||||||
|
return _itemIdChanges.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
|
void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
|
||||||
|
@ -43,6 +95,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const
|
||||||
return _itemRepaintRequest.events();
|
return _itemRepaintRequest.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::requestViewRepaint(not_null<const ViewElement*> view) {
|
||||||
|
_viewRepaintRequest.fire_copy(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<const ViewElement*>> Session::viewRepaintRequest() const {
|
||||||
|
return _viewRepaintRequest.events();
|
||||||
|
}
|
||||||
|
|
||||||
void Session::requestItemViewResize(not_null<const HistoryItem*> item) {
|
void Session::requestItemViewResize(not_null<const HistoryItem*> item) {
|
||||||
_itemViewResizeRequest.fire_copy(item);
|
_itemViewResizeRequest.fire_copy(item);
|
||||||
}
|
}
|
||||||
|
@ -51,6 +111,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemViewResizeRequest() con
|
||||||
return _itemViewResizeRequest.events();
|
return _itemViewResizeRequest.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::requestViewResize(not_null<const ViewElement*> view) {
|
||||||
|
_viewResizeRequest.fire_copy(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<const ViewElement*>> Session::viewResizeRequest() const {
|
||||||
|
return _viewResizeRequest.events();
|
||||||
|
}
|
||||||
|
|
||||||
void Session::requestItemViewRefresh(not_null<const HistoryItem*> item) {
|
void Session::requestItemViewRefresh(not_null<const HistoryItem*> item) {
|
||||||
_itemViewRefreshRequest.fire_copy(item);
|
_itemViewRefreshRequest.fire_copy(item);
|
||||||
}
|
}
|
||||||
|
@ -59,6 +127,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemViewRefreshRequest() co
|
||||||
return _itemViewRefreshRequest.events();
|
return _itemViewRefreshRequest.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::requestItemPlayInline(not_null<const HistoryItem*> item) {
|
||||||
|
_itemPlayInlineRequest.fire_copy(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<not_null<const HistoryItem*>> Session::itemPlayInlineRequest() const {
|
||||||
|
return _itemPlayInlineRequest.events();
|
||||||
|
}
|
||||||
|
|
||||||
void Session::markItemRemoved(not_null<const HistoryItem*> item) {
|
void Session::markItemRemoved(not_null<const HistoryItem*> item) {
|
||||||
_itemRemoved.fire_copy(item);
|
_itemRemoved.fire_copy(item);
|
||||||
}
|
}
|
||||||
|
@ -173,16 +249,9 @@ MessageIdsList Session::itemsToIds(
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageIdsList Session::groupToIds(
|
|
||||||
not_null<HistoryMessageGroup*> group) const {
|
|
||||||
auto result = itemsToIds(group->others);
|
|
||||||
result.push_back(group->leader->fullId());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const {
|
MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const {
|
||||||
if (const auto group = item->getFullGroup()) {
|
if (const auto group = groups().find(item)) {
|
||||||
return groupToIds(group);
|
return itemsToIds(group->items);
|
||||||
}
|
}
|
||||||
return { 1, item->fullId() };
|
return { 1, item->fullId() };
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "chat_helpers/stickers.h"
|
#include "chat_helpers/stickers.h"
|
||||||
#include "dialogs/dialogs_key.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 {
|
namespace Data {
|
||||||
|
|
||||||
|
@ -18,6 +24,8 @@ class Feed;
|
||||||
|
|
||||||
class Session final {
|
class Session final {
|
||||||
public:
|
public:
|
||||||
|
using ViewElement = HistoryView::Element;
|
||||||
|
|
||||||
Session();
|
Session();
|
||||||
~Session();
|
~Session();
|
||||||
|
|
||||||
|
@ -30,6 +38,7 @@ public:
|
||||||
base::Observable<void> &moreChatsLoaded() {
|
base::Observable<void> &moreChatsLoaded() {
|
||||||
return _moreChatsLoaded;
|
return _moreChatsLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Observable<void> &pendingHistoryResize() {
|
base::Observable<void> &pendingHistoryResize() {
|
||||||
return _pendingHistoryResize;
|
return _pendingHistoryResize;
|
||||||
}
|
}
|
||||||
|
@ -40,18 +49,45 @@ public:
|
||||||
base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
|
base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
|
||||||
return _queryItemVisibility;
|
return _queryItemVisibility;
|
||||||
}
|
}
|
||||||
void markItemLayoutChanged(not_null<const HistoryItem*> item);
|
struct IdChange {
|
||||||
|
not_null<HistoryItem*> item;
|
||||||
|
MsgId oldId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void registerItemView(not_null<ViewElement*> view);
|
||||||
|
void unregisterItemView(not_null<ViewElement*> view);
|
||||||
|
template <typename Method>
|
||||||
|
void enumerateItemViews(not_null<HistoryItem*> 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<IdChange> itemIdChanged() const;
|
||||||
|
void markItemLayoutChange(not_null<const HistoryItem*> item);
|
||||||
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
|
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
|
||||||
|
void markViewLayoutChange(not_null<const ViewElement*> view);
|
||||||
|
rpl::producer<not_null<const ViewElement*>> viewLayoutChanged() const;
|
||||||
void requestItemRepaint(not_null<const HistoryItem*> item);
|
void requestItemRepaint(not_null<const HistoryItem*> item);
|
||||||
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
|
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
|
||||||
|
void requestViewRepaint(not_null<const ViewElement*> view);
|
||||||
|
rpl::producer<not_null<const ViewElement*>> viewRepaintRequest() const;
|
||||||
void requestItemViewResize(not_null<const HistoryItem*> item);
|
void requestItemViewResize(not_null<const HistoryItem*> item);
|
||||||
rpl::producer<not_null<const HistoryItem*>> itemViewResizeRequest() const;
|
rpl::producer<not_null<const HistoryItem*>> itemViewResizeRequest() const;
|
||||||
|
void requestViewResize(not_null<const ViewElement*> view);
|
||||||
|
rpl::producer<not_null<const ViewElement*>> viewResizeRequest() const;
|
||||||
void requestItemViewRefresh(not_null<const HistoryItem*> item);
|
void requestItemViewRefresh(not_null<const HistoryItem*> item);
|
||||||
rpl::producer<not_null<const HistoryItem*>> itemViewRefreshRequest() const;
|
rpl::producer<not_null<const HistoryItem*>> itemViewRefreshRequest() const;
|
||||||
|
void requestItemPlayInline(not_null<const HistoryItem*> item);
|
||||||
|
rpl::producer<not_null<const HistoryItem*>> itemPlayInlineRequest() const;
|
||||||
void markItemRemoved(not_null<const HistoryItem*> item);
|
void markItemRemoved(not_null<const HistoryItem*> item);
|
||||||
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
|
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
|
||||||
void markHistoryUnloaded(not_null<const History*> history);
|
void markHistoryUnloaded(not_null<const History*> history);
|
||||||
rpl::producer<not_null<const History*>> historyUnloaded() const;
|
rpl::producer<not_null<const History*>> historyUnloaded() const;
|
||||||
|
|
||||||
void markHistoryCleared(not_null<const History*> history);
|
void markHistoryCleared(not_null<const History*> history);
|
||||||
rpl::producer<not_null<const History*>> historyCleared() const;
|
rpl::producer<not_null<const History*>> historyCleared() const;
|
||||||
using MegagroupParticipant = std::tuple<
|
using MegagroupParticipant = std::tuple<
|
||||||
|
@ -147,7 +183,6 @@ public:
|
||||||
|
|
||||||
HistoryItemsList idsToItems(const MessageIdsList &ids) const;
|
HistoryItemsList idsToItems(const MessageIdsList &ids) const;
|
||||||
MessageIdsList itemsToIds(const HistoryItemsList &items) const;
|
MessageIdsList itemsToIds(const HistoryItemsList &items) const;
|
||||||
MessageIdsList groupToIds(not_null<HistoryMessageGroup*> group) const;
|
|
||||||
MessageIdsList itemOrItsGroup(not_null<HistoryItem*> item) const;
|
MessageIdsList itemOrItsGroup(not_null<HistoryItem*> item) const;
|
||||||
|
|
||||||
int pinnedDialogsCount() const;
|
int pinnedDialogsCount() const;
|
||||||
|
@ -165,6 +200,13 @@ public:
|
||||||
void setMimeForwardIds(MessageIdsList &&list);
|
void setMimeForwardIds(MessageIdsList &&list);
|
||||||
MessageIdsList takeMimeForwardIds();
|
MessageIdsList takeMimeForwardIds();
|
||||||
|
|
||||||
|
Groups &groups() {
|
||||||
|
return _groups;
|
||||||
|
}
|
||||||
|
const Groups &groups() const {
|
||||||
|
return _groups;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const {
|
bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const {
|
||||||
constexpr auto kStickersUpdateTimeout = TimeMs(3600'000);
|
constexpr auto kStickersUpdateTimeout = TimeMs(3600'000);
|
||||||
|
@ -181,10 +223,15 @@ private:
|
||||||
base::Observable<void> _moreChatsLoaded;
|
base::Observable<void> _moreChatsLoaded;
|
||||||
base::Observable<void> _pendingHistoryResize;
|
base::Observable<void> _pendingHistoryResize;
|
||||||
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
|
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanged;
|
rpl::event_stream<IdChange> _itemIdChanges;
|
||||||
|
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanges;
|
||||||
|
rpl::event_stream<not_null<const ViewElement*>> _viewLayoutChanges;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest;
|
rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest;
|
||||||
|
rpl::event_stream<not_null<const ViewElement*>> _viewRepaintRequest;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemViewResizeRequest;
|
rpl::event_stream<not_null<const HistoryItem*>> _itemViewResizeRequest;
|
||||||
|
rpl::event_stream<not_null<const ViewElement*>> _viewResizeRequest;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemViewRefreshRequest;
|
rpl::event_stream<not_null<const HistoryItem*>> _itemViewRefreshRequest;
|
||||||
|
rpl::event_stream<not_null<const HistoryItem*>> _itemPlayInlineRequest;
|
||||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
||||||
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
||||||
rpl::event_stream<not_null<const History*>> _historyCleared;
|
rpl::event_stream<not_null<const History*>> _historyCleared;
|
||||||
|
@ -207,6 +254,10 @@ private:
|
||||||
|
|
||||||
std::deque<Dialogs::Key> _pinnedDialogs;
|
std::deque<Dialogs::Key> _pinnedDialogs;
|
||||||
base::flat_map<FeedId, std::unique_ptr<Data::Feed>> _feeds;
|
base::flat_map<FeedId, std::unique_ptr<Data::Feed>> _feeds;
|
||||||
|
Groups _groups;
|
||||||
|
std::map<
|
||||||
|
not_null<HistoryItem*>,
|
||||||
|
std::vector<not_null<HistoryView::Element*>>> _views;
|
||||||
|
|
||||||
MessageIdsList _mimeForwardIds;
|
MessageIdsList _mimeForwardIds;
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_sparse_ids.h"
|
#include "data/data_sparse_ids.h"
|
||||||
#include "info/info_memento.h"
|
#include "info/info_memento.h"
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
|
@ -322,10 +323,8 @@ base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated(
|
||||||
}
|
}
|
||||||
return LastFullMsgId(ending ? *ending : slice)
|
return LastFullMsgId(ending ? *ending : slice)
|
||||||
| [](FullMsgId msgId) { return App::histItemById(msgId); }
|
| [](FullMsgId msgId) { return App::histItemById(msgId); }
|
||||||
| [](HistoryItem *item) { return item ? item->getMedia() : nullptr; }
|
| [](HistoryItem *item) { return item ? item->media() : nullptr; }
|
||||||
| [](HistoryMedia *media) {
|
| [](Data::Media *media) { return media ? media->photo() : nullptr; }
|
||||||
return media ? media->getPhoto() : nullptr;
|
|
||||||
}
|
|
||||||
| [](PhotoData *photo) { return photo ? photo->id : 0; }
|
| [](PhotoData *photo) { return photo ? photo->id : 0; }
|
||||||
| [&](PhotoId photoId) { return *lastPeerPhotoId != photoId; };
|
| [&](PhotoId photoId) { return *lastPeerPhotoId != photoId; };
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/value_ordering.h"
|
||||||
|
|
||||||
class HistoryItem;
|
class HistoryItem;
|
||||||
using HistoryItemsList = std::vector<not_null<HistoryItem*>>;
|
using HistoryItemsList = std::vector<not_null<HistoryItem*>>;
|
||||||
|
|
||||||
|
@ -22,6 +24,32 @@ struct UploadState {
|
||||||
|
|
||||||
} // namespace Data
|
} // 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<Type>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return value != None;
|
||||||
|
}
|
||||||
|
Underlying raw() const {
|
||||||
|
return static_cast<Underlying>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline Type value_ordering_helper(MessageGroupId value) {
|
||||||
|
return value.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class PeerData;
|
class PeerData;
|
||||||
class UserData;
|
class UserData;
|
||||||
class ChatData;
|
class ChatData;
|
||||||
|
|
|
@ -243,9 +243,10 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
|
||||||
if (auto main = App::main()) {
|
if (auto main = App::main()) {
|
||||||
InvokeQueued(main, [msgId] {
|
InvokeQueued(main, [msgId] {
|
||||||
if (auto item = App::histItemById(msgId)) {
|
if (auto item = App::histItemById(msgId)) {
|
||||||
if (auto media = item->getMedia()) {
|
// #TODO GIFs
|
||||||
media->playInline(true);
|
//if (auto media = item->getMedia()) {
|
||||||
}
|
// media->playInline(true);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "boxes/edit_participant_box.h"
|
#include "boxes/edit_participant_box.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
|
||||||
namespace AdminLog {
|
namespace AdminLog {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -775,7 +776,7 @@ void InnerWidget::paintEmpty(Painter &p) {
|
||||||
|
|
||||||
TextWithEntities InnerWidget::getSelectedText() const {
|
TextWithEntities InnerWidget::getSelectedText() const {
|
||||||
return _selectedItem
|
return _selectedItem
|
||||||
? _selectedItem->data()->selectedText(_selectedText)
|
? _selectedItem->selectedText(_selectedText)
|
||||||
: TextWithEntities();
|
: TextWithEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -911,7 +912,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
const auto item = view ? view->data().get() : nullptr;
|
const auto item = view ? view->data().get() : nullptr;
|
||||||
const auto itemId = item ? item->fullId() : FullMsgId();
|
const auto itemId = item ? item->fullId() : FullMsgId();
|
||||||
bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg());
|
bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg());
|
||||||
bool canForward = item && item->canForward();
|
bool canForward = item && item->allowsForward();
|
||||||
|
|
||||||
auto msg = dynamic_cast<HistoryMessage*>(item);
|
auto msg = dynamic_cast<HistoryMessage*>(item);
|
||||||
if (isUponSelected > 0) {
|
if (isUponSelected > 0) {
|
||||||
|
@ -919,7 +920,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
} else {
|
} else {
|
||||||
if (item && !isUponSelected) {
|
if (item && !isUponSelected) {
|
||||||
auto mediaHasTextForCopy = false;
|
auto mediaHasTextForCopy = false;
|
||||||
if (auto media = (msg ? msg->getMedia() : nullptr)) {
|
if (auto media = view->media()) {
|
||||||
mediaHasTextForCopy = media->hasTextForCopy();
|
mediaHasTextForCopy = media->hasTextForCopy();
|
||||||
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
|
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
|
||||||
media = static_cast<HistoryWebPage*>(media)->attach();
|
media = static_cast<HistoryWebPage*>(media)->attach();
|
||||||
|
@ -1040,8 +1041,8 @@ void InnerWidget::showContextInFolder(not_null<DocumentData*> document) {
|
||||||
|
|
||||||
void InnerWidget::openContextGif(FullMsgId itemId) {
|
void InnerWidget::openContextGif(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (auto media = item->getMedia()) {
|
if (auto media = item->media()) {
|
||||||
if (auto document = media->getDocument()) {
|
if (auto document = media->document()) {
|
||||||
Messenger::Instance().showDocument(document, item);
|
Messenger::Instance().showDocument(document, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1050,13 +1051,10 @@ void InnerWidget::openContextGif(FullMsgId itemId) {
|
||||||
|
|
||||||
void InnerWidget::copyContextText(FullMsgId itemId) {
|
void InnerWidget::copyContextText(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto view = viewForItem(item)) {
|
||||||
if (media->type() == MediaTypeSticker) {
|
setToClipboard(view->selectedText(FullSelection));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setToClipboard(item->selectedText(FullSelection));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::setToClipboard(
|
void InnerWidget::setToClipboard(
|
||||||
|
@ -1416,7 +1414,7 @@ void InnerWidget::updateSelected() {
|
||||||
}
|
}
|
||||||
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
||||||
if (_mouseSelectType != TextSelectType::Letters) {
|
if (_mouseSelectType != TextSelectType::Letters) {
|
||||||
selection = _mouseActionItem->data()->adjustSelection(
|
selection = _mouseActionItem->adjustSelection(
|
||||||
selection,
|
selection,
|
||||||
_mouseSelectType);
|
_mouseSelectType);
|
||||||
}
|
}
|
||||||
|
@ -1517,17 +1515,21 @@ void InnerWidget::performDrag() {
|
||||||
// auto forwardMimeType = QString();
|
// auto forwardMimeType = QString();
|
||||||
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
||||||
// if (auto pressedItem = App::pressedItem()) {
|
// if (auto pressedItem = App::pressedItem()) {
|
||||||
// pressedMedia = pressedItem->getMedia();
|
// pressedMedia = pressedItem->media();
|
||||||
// if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) {
|
// if (_mouseCursorState == HistoryInDateCursorState
|
||||||
|
// || (pressedMedia && pressedMedia->dragItem())) {
|
||||||
// forwardMimeType = qsl("application/x-td-forward");
|
// 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 (auto pressedLnkItem = App::pressedLinkItem()) {
|
||||||
// if ((pressedMedia = pressedLnkItem->getMedia())) {
|
// if ((pressedMedia = pressedLnkItem->media())) {
|
||||||
// if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
|
// if (forwardMimeType.isEmpty()
|
||||||
|
// && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||||
// forwardMimeType = qsl("application/x-td-forward");
|
// forwardMimeType = qsl("application/x-td-forward");
|
||||||
// Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() });
|
// Auth().data().setMimeForwardIds(
|
||||||
|
// { 1, pressedLnkItem->fullId() });
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -422,9 +422,13 @@ void GenerateItems(
|
||||||
auto text = lng_admin_log_pinned_message(lt_from, fromLinkText);
|
auto text = lng_admin_log_pinned_message(lt_from, fromLinkText);
|
||||||
addSimpleServiceMessage(text);
|
addSimpleServiceMessage(text);
|
||||||
|
|
||||||
auto applyServiceAction = false;
|
|
||||||
auto detachExistingItem = 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);
|
addSimpleServiceMessage(text);
|
||||||
|
|
||||||
auto oldValue = ExtractEditedText(action.vprev_message);
|
auto oldValue = ExtractEditedText(action.vprev_message);
|
||||||
auto applyServiceAction = false;
|
|
||||||
auto detachExistingItem = 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()) {
|
if (oldValue.text.isEmpty()) {
|
||||||
oldValue = PrepareText(QString(), lang(lng_admin_log_empty_text));
|
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);
|
auto text = lng_admin_log_deleted_message(lt_from, fromLinkText);
|
||||||
addSimpleServiceMessage(text);
|
addSimpleServiceMessage(text);
|
||||||
|
|
||||||
auto applyServiceAction = false;
|
|
||||||
auto detachExistingItem = 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 = [&]() {
|
auto createParticipantJoin = [&]() {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -164,7 +164,9 @@ public:
|
||||||
not_null<HistoryItem*> addNewGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup);
|
not_null<HistoryItem*> 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.
|
// 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<MTPMessage> &slice);
|
void addOlderSlice(const QVector<MTPMessage> &slice);
|
||||||
void addNewerSlice(const QVector<MTPMessage> &slice);
|
void addNewerSlice(const QVector<MTPMessage> &slice);
|
||||||
|
@ -400,9 +402,11 @@ protected:
|
||||||
not_null<HistoryItem*> 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<HistoryItem*> 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<HistoryItem*> createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup);
|
not_null<HistoryItem*> createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup);
|
||||||
|
|
||||||
not_null<HistoryItem*> addNewItem(not_null<HistoryItem*> adding, bool newMsg);
|
not_null<HistoryItem*> addNewItem(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
bool unread);
|
||||||
not_null<HistoryItem*> addNewInTheMiddle(
|
not_null<HistoryItem*> addNewInTheMiddle(
|
||||||
not_null<HistoryItem*> newItem,
|
not_null<HistoryItem*> item,
|
||||||
int blockIndex,
|
int blockIndex,
|
||||||
int itemIndex);
|
int itemIndex);
|
||||||
|
|
||||||
|
@ -429,6 +433,13 @@ private:
|
||||||
void changedInChatListHook(Dialogs::Mode list, bool added) override;
|
void changedInChatListHook(Dialogs::Mode list, bool added) override;
|
||||||
void changedChatListPinHook() override;
|
void changedChatListPinHook() override;
|
||||||
|
|
||||||
|
void applyMessageChanges(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const MTPMessage &original);
|
||||||
|
void applyServiceChanges(
|
||||||
|
not_null<HistoryItem*> item,
|
||||||
|
const MTPDmessageService &data);
|
||||||
|
|
||||||
// After adding a new history slice check the lastMsg and newLoaded.
|
// After adding a new history slice check the lastMsg and newLoaded.
|
||||||
void checkLastMsg();
|
void checkLastMsg();
|
||||||
|
|
||||||
|
@ -441,17 +452,19 @@ private:
|
||||||
|
|
||||||
void clearSendAction(not_null<UserData*> from);
|
void clearSendAction(not_null<UserData*> from);
|
||||||
|
|
||||||
HistoryItem *findPreviousItem(not_null<HistoryItem*> item) const;
|
HistoryView::Element *findPreviousItem(
|
||||||
HistoryItem *findNextItem(not_null<HistoryItem*> item) const;
|
not_null<HistoryView::Element*> view) const;
|
||||||
not_null<HistoryItem*> findGroupFirst(
|
HistoryView::Element *findNextItem(
|
||||||
not_null<HistoryItem*> item) const;
|
not_null<HistoryView::Element*> view) const;
|
||||||
not_null<HistoryItem*> findGroupLast(
|
not_null<HistoryView::Element*> findGroupFirst(
|
||||||
not_null<HistoryItem*> item) const;
|
not_null<HistoryView::Element*> view) const;
|
||||||
auto recountGroupingFromTill(not_null<HistoryItem*> item)
|
not_null<HistoryView::Element*> findGroupLast(
|
||||||
-> std::pair<not_null<HistoryItem*>, not_null<HistoryItem*>>;
|
not_null<HistoryView::Element*> view) const;
|
||||||
|
auto recountGroupingFromTill(not_null<HistoryView::Element*> view)
|
||||||
|
-> std::pair<not_null<HistoryView::Element*>, not_null<HistoryView::Element*>>;
|
||||||
void recountGrouping(
|
void recountGrouping(
|
||||||
not_null<HistoryItem*> from,
|
not_null<HistoryView::Element*> from,
|
||||||
not_null<HistoryItem*> till);
|
not_null<HistoryView::Element*> till);
|
||||||
|
|
||||||
enum class Flag {
|
enum class Flag {
|
||||||
f_has_pending_resized_items = (1 << 0),
|
f_has_pending_resized_items = (1 << 0),
|
||||||
|
|
|
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -405,7 +406,7 @@ void HistoryInner::enumerateDates(Method method) {
|
||||||
|
|
||||||
TextSelection HistoryInner::computeRenderSelection(
|
TextSelection HistoryInner::computeRenderSelection(
|
||||||
not_null<const SelectedItems*> selected,
|
not_null<const SelectedItems*> selected,
|
||||||
not_null<HistoryItem*> item) const {
|
not_null<Element*> view) const {
|
||||||
const auto itemSelection = [&](not_null<HistoryItem*> item) {
|
const auto itemSelection = [&](not_null<HistoryItem*> item) {
|
||||||
auto i = selected->find(item);
|
auto i = selected->find(item);
|
||||||
if (i != selected->end()) {
|
if (i != selected->end()) {
|
||||||
|
@ -413,22 +414,22 @@ TextSelection HistoryInner::computeRenderSelection(
|
||||||
}
|
}
|
||||||
return TextSelection();
|
return TextSelection();
|
||||||
};
|
};
|
||||||
const auto group = item->Get<HistoryMessageGroup>();
|
const auto group = view->Get<HistoryView::Group>();
|
||||||
if (group) {
|
if (group) {
|
||||||
if (group->leader != item) {
|
if (group->leader != view) {
|
||||||
return TextSelection();
|
return TextSelection();
|
||||||
}
|
}
|
||||||
auto result = TextSelection();
|
auto result = TextSelection();
|
||||||
auto allFullSelected = true;
|
auto allFullSelected = true;
|
||||||
const auto count = int(group->others.size());
|
const auto count = int(group->others.size());
|
||||||
for (auto i = 0; i != count; ++i) {
|
for (auto i = 0; i != count; ++i) {
|
||||||
if (itemSelection(group->others[i]) == FullSelection) {
|
if (itemSelection(group->others[i]->data()) == FullSelection) {
|
||||||
result = AddGroupItemSelection(result, i);
|
result = AddGroupItemSelection(result, i);
|
||||||
} else {
|
} else {
|
||||||
allFullSelected = false;
|
allFullSelected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto leaderSelection = itemSelection(item);
|
const auto leaderSelection = itemSelection(view->data());
|
||||||
if (leaderSelection == FullSelection) {
|
if (leaderSelection == FullSelection) {
|
||||||
return allFullSelected
|
return allFullSelected
|
||||||
? FullSelection
|
? FullSelection
|
||||||
|
@ -438,7 +439,7 @@ TextSelection HistoryInner::computeRenderSelection(
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return itemSelection(item);
|
return itemSelection(view->data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TextSelection HistoryInner::itemRenderSelection(
|
TextSelection HistoryInner::itemRenderSelection(
|
||||||
|
@ -452,7 +453,7 @@ TextSelection HistoryInner::itemRenderSelection(
|
||||||
return FullSelection;
|
return FullSelection;
|
||||||
}
|
}
|
||||||
} else if (!_selected.empty()) {
|
} else if (!_selected.empty()) {
|
||||||
return computeRenderSelection(&_selected, item);
|
return computeRenderSelection(&_selected, view);
|
||||||
}
|
}
|
||||||
return TextSelection();
|
return TextSelection();
|
||||||
}
|
}
|
||||||
|
@ -528,7 +529,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
if (item->mentionsMe() && item->isMediaUnread()) {
|
if (item->mentionsMe() && item->isMediaUnread()) {
|
||||||
readMentions.insert(item);
|
readMentions.insert(item);
|
||||||
_widget->enqueueMessageHighlight(item);
|
_widget->enqueueMessageHighlight(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 h = view->height();
|
int32 h = view->height();
|
||||||
|
@ -574,7 +575,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
if (item->mentionsMe() && item->isMediaUnread()) {
|
if (item->mentionsMe() && item->isMediaUnread()) {
|
||||||
readMentions.insert(item);
|
readMentions.insert(item);
|
||||||
_widget->enqueueMessageHighlight(item);
|
_widget->enqueueMessageHighlight(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.translate(0, h);
|
p.translate(0, h);
|
||||||
|
@ -984,7 +985,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
|
||||||
if (uponSelected) {
|
if (uponSelected) {
|
||||||
_mouseAction = MouseAction::PrepareDrag; // start text drag
|
_mouseAction = MouseAction::PrepareDrag; // start text drag
|
||||||
} else if (!_pressWasInactive) {
|
} else if (!_pressWasInactive) {
|
||||||
if (dynamic_cast<HistorySticker*>(App::pressedItem()->data()->getMedia()) || _mouseCursorState == HistoryInDateCursorState) {
|
if (dynamic_cast<HistorySticker*>(App::pressedItem()->media()) || _mouseCursorState == HistoryInDateCursorState) {
|
||||||
_mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag
|
_mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag
|
||||||
} else {
|
} else {
|
||||||
if (dragState.afterSymbol) ++_mouseTextSymbol;
|
if (dragState.afterSymbol) ++_mouseTextSymbol;
|
||||||
|
@ -1089,20 +1090,26 @@ void HistoryInner::performDrag() {
|
||||||
auto forwardMimeType = QString();
|
auto forwardMimeType = QString();
|
||||||
auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
||||||
if (auto pressedItem = App::pressedItem()) {
|
if (auto pressedItem = App::pressedItem()) {
|
||||||
pressedMedia = pressedItem->data()->getMedia();
|
pressedMedia = pressedItem->media();
|
||||||
if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) {
|
if (_mouseCursorState == HistoryInDateCursorState
|
||||||
Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem->data()));
|
|| (pressedMedia && pressedMedia->dragItem())) {
|
||||||
|
Auth().data().setMimeForwardIds(
|
||||||
|
Auth().data().itemOrItsGroup(pressedItem->data()));
|
||||||
forwardMimeType = qsl("application/x-td-forward");
|
forwardMimeType = qsl("application/x-td-forward");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (const auto pressedLnkItem = _mouseActionItem) {
|
if (const auto pressedLnkItem = _mouseActionItem) {
|
||||||
if ((pressedMedia = pressedLnkItem->getMedia())) {
|
if (const auto view = pressedLnkItem->mainView()) {
|
||||||
if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
|
if ((pressedMedia = view->media())) {
|
||||||
Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() });
|
if (forwardMimeType.isEmpty()
|
||||||
|
&& pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||||
|
Auth().data().setMimeForwardIds(
|
||||||
|
{ 1, pressedLnkItem->fullId() });
|
||||||
forwardMimeType = qsl("application/x-td-forward");
|
forwardMimeType = qsl("application/x-td-forward");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!forwardMimeType.isEmpty()) {
|
if (!forwardMimeType.isEmpty()) {
|
||||||
auto mimeData = std::make_unique<QMimeData>();
|
auto mimeData = std::make_unique<QMimeData>();
|
||||||
mimeData->setData(forwardMimeType, "1");
|
mimeData->setData(forwardMimeType, "1");
|
||||||
|
@ -1162,13 +1169,15 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
||||||
// if we are in selecting items mode perhaps we want to
|
// if we are in selecting items mode perhaps we want to
|
||||||
// toggle selection instead of activating the pressed link
|
// toggle selection instead of activating the pressed link
|
||||||
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) {
|
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) {
|
||||||
if (auto media = _mouseActionItem->getMedia()) {
|
if (const auto view = _mouseActionItem->mainView()) {
|
||||||
|
if (const auto media = view->media()) {
|
||||||
if (media->toggleSelectionByHandlerClick(activated)) {
|
if (media->toggleSelectionByHandlerClick(activated)) {
|
||||||
activated = nullptr;
|
activated = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (App::pressedItem()) {
|
if (App::pressedItem()) {
|
||||||
repaintItem(App::pressedItem());
|
repaintItem(App::pressedItem());
|
||||||
App::pressedItem(nullptr);
|
App::pressedItem(nullptr);
|
||||||
|
@ -1232,7 +1241,11 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
||||||
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
||||||
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
||||||
const auto [item, selection] = *_selected.cbegin();
|
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
|
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
|
||||||
}
|
}
|
||||||
|
@ -1340,7 +1353,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
_widget->replyToMessage(itemId);
|
_widget->replyToMessage(itemId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (item->canEdit(::date(unixtime()))) {
|
if (item->allowsEdit(::date(unixtime()))) {
|
||||||
_menu->addAction(lang(lng_context_edit_msg), [=] {
|
_menu->addAction(lang(lng_context_edit_msg), [=] {
|
||||||
_widget->editMessage(itemId);
|
_widget->editMessage(itemId);
|
||||||
});
|
});
|
||||||
|
@ -1423,7 +1436,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
} else if (item) {
|
} else if (item) {
|
||||||
const auto itemId = item->fullId();
|
const auto itemId = item->fullId();
|
||||||
if (isUponSelected != -2) {
|
if (isUponSelected != -2) {
|
||||||
if (item->canForward()) {
|
if (item->allowsForward()) {
|
||||||
_menu->addAction(lang(lng_context_forward_msg), [=] {
|
_menu->addAction(lang(lng_context_forward_msg), [=] {
|
||||||
forwardItem(itemId);
|
forwardItem(itemId);
|
||||||
})->setEnabled(true);
|
})->setEnabled(true);
|
||||||
|
@ -1453,10 +1466,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
: App::hoveredLinkItem()
|
: App::hoveredLinkItem()
|
||||||
? App::hoveredLinkItem()->data().get()
|
? App::hoveredLinkItem()->data().get()
|
||||||
: nullptr) {
|
: nullptr) {
|
||||||
if (const auto group = result->getFullGroup()) {
|
if (const auto group = Auth().data().groups().find(result)) {
|
||||||
return group->others.empty()
|
return group->items.front();
|
||||||
? group->leader
|
|
||||||
: group->others.front().get();
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1466,8 +1477,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
const auto canDelete = item
|
const auto canDelete = item
|
||||||
&& item->canDelete()
|
&& item->canDelete()
|
||||||
&& (item->id > 0 || !item->serviceMsg());
|
&& (item->id > 0 || !item->serviceMsg());
|
||||||
const auto canForward = item
|
const auto canForward = item && item->allowsForward();
|
||||||
&& item->canForward();
|
const auto view = item ? item->mainView() : nullptr;
|
||||||
|
|
||||||
const auto msg = dynamic_cast<HistoryMessage*>(item);
|
const auto msg = dynamic_cast<HistoryMessage*>(item);
|
||||||
if (isUponSelected > 0) {
|
if (isUponSelected > 0) {
|
||||||
|
@ -1477,7 +1488,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
addItemActions(item);
|
addItemActions(item);
|
||||||
if (item && !isUponSelected) {
|
if (item && !isUponSelected) {
|
||||||
auto mediaHasTextForCopy = false;
|
auto mediaHasTextForCopy = false;
|
||||||
if (auto media = (msg ? msg->getMedia() : nullptr)) {
|
if (auto media = (view ? view->media() : nullptr)) {
|
||||||
mediaHasTextForCopy = media->hasTextForCopy();
|
mediaHasTextForCopy = media->hasTextForCopy();
|
||||||
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
|
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
|
||||||
media = static_cast<HistoryWebPage*>(media)->attach();
|
media = static_cast<HistoryWebPage*>(media)->attach();
|
||||||
|
@ -1665,8 +1676,8 @@ void HistoryInner::saveDocumentToFile(not_null<DocumentData*> document) {
|
||||||
|
|
||||||
void HistoryInner::openContextGif(FullMsgId itemId) {
|
void HistoryInner::openContextGif(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto document = media->getDocument()) {
|
if (const auto document = media->document()) {
|
||||||
Messenger::Instance().showDocument(document, item);
|
Messenger::Instance().showDocument(document, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1675,8 +1686,8 @@ void HistoryInner::openContextGif(FullMsgId itemId) {
|
||||||
|
|
||||||
void HistoryInner::saveContextGif(FullMsgId itemId) {
|
void HistoryInner::saveContextGif(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto document = media->getDocument()) {
|
if (const auto document = media->document()) {
|
||||||
_widget->saveGif(document);
|
_widget->saveGif(document);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1685,15 +1696,12 @@ void HistoryInner::saveContextGif(FullMsgId itemId) {
|
||||||
|
|
||||||
void HistoryInner::copyContextText(FullMsgId itemId) {
|
void HistoryInner::copyContextText(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto view = item->mainView()) {
|
||||||
if (media->type() == MediaTypeSticker) {
|
const auto group = view->getFullGroup();
|
||||||
return;
|
const auto leader = group ? group->leader : view;
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto group = item->getFullGroup();
|
|
||||||
const auto leader = group ? group->leader : item;
|
|
||||||
setToClipboard(leader->selectedText(FullSelection));
|
setToClipboard(leader->selectedText(FullSelection));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryInner::setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode) {
|
void HistoryInner::setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode) {
|
||||||
|
@ -1718,26 +1726,30 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
||||||
}
|
}
|
||||||
if (selected.cbegin()->second != FullSelection) {
|
if (selected.cbegin()->second != FullSelection) {
|
||||||
const auto [item, selection] = *selected.cbegin();
|
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");
|
const auto timeFormat = qsl(", [dd.MM.yy hh:mm]\n");
|
||||||
auto groupLeadersAdded = base::flat_set<not_null<HistoryItem*>>();
|
auto groupLeadersAdded = base::flat_set<not_null<Element*>>();
|
||||||
auto fullSize = 0;
|
auto fullSize = 0;
|
||||||
auto texts = base::flat_map<std::pair<int, MsgId>, TextWithEntities>();
|
auto texts = base::flat_map<std::pair<int, MsgId>, TextWithEntities>();
|
||||||
|
|
||||||
const auto addItem = [&](
|
const auto addItem = [&](
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryView::Element*> view,
|
||||||
TextSelection selection) {
|
TextSelection selection) {
|
||||||
|
const auto item = view->data();
|
||||||
auto time = item->date.toString(timeFormat);
|
auto time = item->date.toString(timeFormat);
|
||||||
auto part = TextWithEntities();
|
auto part = TextWithEntities();
|
||||||
auto unwrapped = item->selectedText(selection);
|
auto unwrapped = view->selectedText(selection);
|
||||||
auto size = item->author()->name.size()
|
auto size = item->author()->name.size()
|
||||||
+ time.size()
|
+ time.size()
|
||||||
+ unwrapped.text.size();
|
+ unwrapped.text.size();
|
||||||
part.text.reserve(size);
|
part.text.reserve(size);
|
||||||
|
|
||||||
auto y = itemTop(item);
|
auto y = itemTop(view);
|
||||||
if (y >= 0) {
|
if (y >= 0) {
|
||||||
part.text.append(item->author()->name).append(time);
|
part.text.append(item->author()->name).append(time);
|
||||||
TextUtilities::Append(part, std::move(unwrapped));
|
TextUtilities::Append(part, std::move(unwrapped));
|
||||||
|
@ -1747,11 +1759,12 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto [item, selection] : selected) {
|
for (const auto [item, selection] : selected) {
|
||||||
if (!item->mainView()) {
|
const auto view = item->mainView();
|
||||||
|
if (!view) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto group = item->Get<HistoryMessageGroup>()) {
|
if (const auto group = view->getFullGroup()) {
|
||||||
if (groupLeadersAdded.contains(group->leader)) {
|
if (groupLeadersAdded.contains(group->leader)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1761,16 +1774,16 @@ TextWithEntities HistoryInner::getSelectedText() const {
|
||||||
if (leaderSelection == FullSelection) {
|
if (leaderSelection == FullSelection) {
|
||||||
groupLeadersAdded.emplace(group->leader);
|
groupLeadersAdded.emplace(group->leader);
|
||||||
addItem(group->leader, FullSelection);
|
addItem(group->leader, FullSelection);
|
||||||
} else if (item == group->leader) {
|
} else if (view == group->leader) {
|
||||||
const auto leaderFullSelection = AddGroupItemSelection(
|
const auto leaderFullSelection = AddGroupItemSelection(
|
||||||
TextSelection(),
|
TextSelection(),
|
||||||
int(group->others.size()));
|
int(group->others.size()));
|
||||||
addItem(item, leaderFullSelection);
|
addItem(view, leaderFullSelection);
|
||||||
} else {
|
} else {
|
||||||
addItem(item, FullSelection);
|
addItem(view, FullSelection);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addItem(item, FullSelection);
|
addItem(view, FullSelection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2174,7 +2187,7 @@ auto HistoryInner::getSelectionState() const
|
||||||
if (selected.first->canDelete()) {
|
if (selected.first->canDelete()) {
|
||||||
++result.canDeleteCount;
|
++result.canDeleteCount;
|
||||||
}
|
}
|
||||||
if (selected.first->canForward()) {
|
if (selected.first->allowsForward()) {
|
||||||
++result.canForwardCount;
|
++result.canForwardCount;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -2411,7 +2424,9 @@ void HistoryInner::onUpdateSelected() {
|
||||||
}
|
}
|
||||||
auto selState = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
auto selState = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
||||||
if (_mouseSelectType != TextSelectType::Letters) {
|
if (_mouseSelectType != TextSelectType::Letters) {
|
||||||
selState = _mouseActionItem->adjustSelection(selState, _mouseSelectType);
|
if (const auto view = _mouseActionItem->mainView()) {
|
||||||
|
selState = view->adjustSelection(selState, _mouseSelectType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (_selected[_mouseActionItem] != selState) {
|
if (_selected[_mouseActionItem] != selState) {
|
||||||
_selected[_mouseActionItem] = selState;
|
_selected[_mouseActionItem] = selState;
|
||||||
|
@ -2570,7 +2585,7 @@ int HistoryInner::itemTop(const HistoryItem *item) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int HistoryInner::itemTop(const Element *view) const {
|
int HistoryInner::itemTop(const Element *view) const {
|
||||||
if (!view) {
|
if (!view || view->data()->mainView() != view) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2609,12 +2624,14 @@ int HistoryInner::moveScrollFollowingInlineKeyboard(
|
||||||
const HistoryItem *item,
|
const HistoryItem *item,
|
||||||
int oldKeyboardTop,
|
int oldKeyboardTop,
|
||||||
int newKeyboardTop) {
|
int newKeyboardTop) {
|
||||||
if (item->isUnderCursor()) {
|
if (const auto view = item ? item->mainView() : nullptr) {
|
||||||
|
if (view->isUnderCursor()) {
|
||||||
const auto top = itemTop(item);
|
const auto top = itemTop(item);
|
||||||
if (top >= oldKeyboardTop) {
|
if (top >= oldKeyboardTop) {
|
||||||
return newKeyboardTop - oldKeyboardTop;
|
return newKeyboardTop - oldKeyboardTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2632,11 +2649,8 @@ bool HistoryInner::isSelected(
|
||||||
bool HistoryInner::isSelectedAsGroup(
|
bool HistoryInner::isSelectedAsGroup(
|
||||||
not_null<SelectedItems*> toItems,
|
not_null<SelectedItems*> toItems,
|
||||||
not_null<HistoryItem*> item) const {
|
not_null<HistoryItem*> item) const {
|
||||||
if (const auto group = item->getFullGroup()) {
|
if (const auto group = Auth().data().groups().find(item)) {
|
||||||
if (!isSelected(toItems, group->leader)) {
|
for (const auto other : group->items) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto other : group->others) {
|
|
||||||
if (!isSelected(toItems, other)) {
|
if (!isSelected(toItems, other)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2703,7 +2717,7 @@ void HistoryInner::changeSelectionAsGroup(
|
||||||
not_null<SelectedItems*> toItems,
|
not_null<SelectedItems*> toItems,
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
SelectAction action) const {
|
SelectAction action) const {
|
||||||
const auto group = item->getFullGroup();
|
const auto group = Auth().data().groups().find(item);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
return changeSelection(toItems, item, action);
|
return changeSelection(toItems, item, action);
|
||||||
}
|
}
|
||||||
|
@ -2716,10 +2730,7 @@ void HistoryInner::changeSelectionAsGroup(
|
||||||
const auto add = (action == SelectAction::Select);
|
const auto add = (action == SelectAction::Select);
|
||||||
|
|
||||||
const auto adding = [&] {
|
const auto adding = [&] {
|
||||||
if (!add || !goodForSelection(toItems, group->leader, total)) {
|
for (const auto other : group->items) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto other : group->others) {
|
|
||||||
if (!goodForSelection(toItems, other, total)) {
|
if (!goodForSelection(toItems, other, total)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2727,13 +2738,11 @@ void HistoryInner::changeSelectionAsGroup(
|
||||||
return (total <= MaxSelectedItems);
|
return (total <= MaxSelectedItems);
|
||||||
}();
|
}();
|
||||||
if (adding) {
|
if (adding) {
|
||||||
addToSelection(toItems, group->leader);
|
for (const auto other : group->items) {
|
||||||
for (const auto other : group->others) {
|
|
||||||
addToSelection(toItems, other);
|
addToSelection(toItems, other);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
removeFromSelection(toItems, group->leader);
|
for (const auto other : group->items) {
|
||||||
for (const auto other : group->others) {
|
|
||||||
removeFromSelection(toItems, other);
|
removeFromSelection(toItems, other);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2745,13 +2754,7 @@ void HistoryInner::forwardItem(FullMsgId itemId) {
|
||||||
|
|
||||||
void HistoryInner::forwardAsGroup(FullMsgId itemId) {
|
void HistoryInner::forwardAsGroup(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto group = item->getFullGroup()) {
|
Window::ShowForwardMessagesBox(Auth().data().itemOrItsGroup(item));
|
||||||
auto items = Auth().data().itemsToIds(group->others);
|
|
||||||
items.push_back(group->leader->fullId());
|
|
||||||
Window::ShowForwardMessagesBox(std::move(items));
|
|
||||||
} else {
|
|
||||||
Window::ShowForwardMessagesBox({ 1, itemId });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2779,13 +2782,12 @@ bool HistoryInner::hasPendingResizedItems() const {
|
||||||
|
|
||||||
void HistoryInner::deleteAsGroup(FullMsgId itemId) {
|
void HistoryInner::deleteAsGroup(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
const auto group = item->getFullGroup();
|
const auto group = Auth().data().groups().find(item);
|
||||||
if (!group || group->others.empty()) {
|
if (!group || group->items.size() < 2) {
|
||||||
return deleteItem(item);
|
return deleteItem(item);
|
||||||
}
|
}
|
||||||
auto items = Auth().data().itemsToIds(group->others);
|
Ui::show(Box<DeleteMessagesBox>(
|
||||||
items.push_back(group->leader->fullId());
|
Auth().data().itemsToIds(group->items)));
|
||||||
Ui::show(Box<DeleteMessagesBox>(Auth().data().groupToIds(group)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2866,7 +2868,7 @@ QString HistoryInner::tooltipText() const {
|
||||||
if (const auto view = App::hoveredItem()) {
|
if (const auto view = App::hoveredItem()) {
|
||||||
auto dateText = view->data()->date.toString(
|
auto dateText = view->data()->date.toString(
|
||||||
QLocale::system().dateTimeFormat(QLocale::LongFormat));
|
QLocale::system().dateTimeFormat(QLocale::LongFormat));
|
||||||
auto editedDate = view->data()->displayedEditDate();
|
auto editedDate = view->displayedEditDate();
|
||||||
if (!editedDate.isNull()) {
|
if (!editedDate.isNull()) {
|
||||||
dateText += '\n' + lng_edited_date(lt_date, editedDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
|
dateText += '\n' + lng_edited_date(lt_date, editedDate.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,7 +212,7 @@ private:
|
||||||
int seltoy) const;
|
int seltoy) const;
|
||||||
TextSelection computeRenderSelection(
|
TextSelection computeRenderSelection(
|
||||||
not_null<const SelectedItems*> selected,
|
not_null<const SelectedItems*> selected,
|
||||||
not_null<HistoryItem*> item) const;
|
not_null<Element*> view) const;
|
||||||
|
|
||||||
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
|
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_media_grouped.h"
|
#include "history/history_media_grouped.h"
|
||||||
|
#include "history/history_service.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "media/media_clip_reader.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 "core/crash_reports.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
|
|
||||||
namespace internal {
|
namespace {
|
||||||
|
|
||||||
TextSelection unshiftSelection(TextSelection selection, uint16 byLength) {
|
not_null<HistoryItem*> CreateUnsupportedMessage(
|
||||||
if (selection == FullSelection) {
|
not_null<History*> history,
|
||||||
return selection;
|
MsgId msgId,
|
||||||
}
|
MTPDmessage::Flags flags,
|
||||||
return ::unshiftSelection(selection, byLength);
|
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) {
|
} // namespace
|
||||||
if (selection == FullSelection) {
|
|
||||||
return selection;
|
|
||||||
}
|
|
||||||
return ::shiftSelection(selection, byLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
HistoryItem::HistoryItem(
|
HistoryItem::HistoryItem(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
|
@ -93,6 +109,12 @@ void HistoryItem::finishEdition(int oldKeyboardTop) {
|
||||||
App::historyUpdateDependent(this);
|
App::historyUpdateDependent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryItem::setGroupId(MessageGroupId groupId) {
|
||||||
|
Auth().data().groups().unregisterMessage(this);
|
||||||
|
_groupId = groupId;
|
||||||
|
Auth().data().groups().registerMessage(this);
|
||||||
|
}
|
||||||
|
|
||||||
HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() {
|
HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() {
|
||||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||||
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
||||||
|
@ -189,12 +211,17 @@ MTPDreplyKeyboardMarkup::Flags HistoryItem::replyKeyboardFlags() const {
|
||||||
return MTPDreplyKeyboardMarkup_ClientFlag::f_zero | 0;
|
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());
|
Expects(isLogEntry());
|
||||||
|
|
||||||
AddComponents(HistoryMessageLogEntryOriginal::Bit());
|
AddComponents(HistoryMessageLogEntryOriginal::Bit());
|
||||||
auto original = Get<HistoryMessageLogEntryOriginal>();
|
Get<HistoryMessageLogEntryOriginal>()->page = App::feedWebPage(
|
||||||
auto webpage = App::feedWebPage(localId, label, content);
|
localId,
|
||||||
original->_page = std::make_unique<HistoryWebPage>(this, webpage);
|
label,
|
||||||
|
content);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserData *HistoryItem::viaBot() const {
|
UserData *HistoryItem::viaBot() const {
|
||||||
|
@ -276,20 +303,11 @@ void HistoryItem::removeMainView() {
|
||||||
|
|
||||||
void HistoryItem::clearMainView() {
|
void HistoryItem::clearMainView() {
|
||||||
_mainView = nullptr;
|
_mainView = nullptr;
|
||||||
|
|
||||||
validateGroupId();
|
|
||||||
if (groupId()) {
|
|
||||||
makeGroupLeader({});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryItem::addToUnreadMentions(UnreadMentionType type) {
|
void HistoryItem::addToUnreadMentions(UnreadMentionType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryItem::indexAsNewItem() {
|
void HistoryItem::indexAsNewItem() {
|
||||||
if (IsServerMsgId(id)) {
|
if (IsServerMsgId(id)) {
|
||||||
CrashReports::SetAnnotation("addToUnreadMentions", QString::number(id));
|
CrashReports::SetAnnotation("addToUnreadMentions", QString::number(id));
|
||||||
|
@ -307,27 +325,21 @@ void HistoryItem::indexAsNewItem() {
|
||||||
void HistoryItem::setRealId(MsgId newId) {
|
void HistoryItem::setRealId(MsgId newId) {
|
||||||
Expects(!IsServerMsgId(id));
|
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
|
// We don't need to call Notify::replyMarkupUpdated(this) and update keyboard
|
||||||
// in history widget, because it can't exist for an outgoing message.
|
// in history widget, because it can't exist for an outgoing message.
|
||||||
// Only inline keyboards can be in outgoing messages.
|
// Only inline keyboards can be in outgoing messages.
|
||||||
if (auto markup = inlineReplyMarkup()) {
|
if (const auto markup = inlineReplyMarkup()) {
|
||||||
if (markup->inlineKeyboard) {
|
if (markup->inlineKeyboard) {
|
||||||
markup->inlineKeyboard->updateMessageId();
|
markup->inlineKeyboard->updateMessageId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_media) {
|
Auth().data().markItemIdChange({ this, oldId });
|
||||||
_media->refreshParentId(this);
|
Auth().data().requestItemRepaint(this);
|
||||||
if (const auto group = Get<HistoryMessageGroup>()) {
|
|
||||||
if (group->leader != this) {
|
|
||||||
if (const auto media = group->leader->getMedia()) {
|
|
||||||
media->refreshParentId(group->leader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryItem::isPinned() const {
|
bool HistoryItem::isPinned() const {
|
||||||
|
@ -347,60 +359,11 @@ bool HistoryItem::canPin() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryItem::canForward() const {
|
bool HistoryItem::allowsForward() const {
|
||||||
if (id < 0 || isLogEntry()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (auto message = toHistoryMessage()) {
|
|
||||||
if (auto media = message->getMedia()) {
|
|
||||||
if (media->type() == MediaTypeCall) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryItem::canEdit(const QDateTime &cur) const {
|
bool HistoryItem::allowsEdit(const QDateTime &now) 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<HistoryMessageVia>() || message->Has<HistoryMessageForwarded>()) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,8 +406,8 @@ bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const {
|
||||||
if (!toHistoryMessage()) {
|
if (!toHistoryMessage()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (auto media = getMedia()) {
|
if (const auto media = this->media()) {
|
||||||
if (media->type() == MediaTypeCall) {
|
if (!media->allowsRevoke()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,8 +456,8 @@ QString HistoryItem::directLink() const {
|
||||||
Assert(channel != nullptr);
|
Assert(channel != nullptr);
|
||||||
auto query = channel->username + '/' + QString::number(id);
|
auto query = channel->username + '/' + QString::number(id);
|
||||||
if (!channel->isMegagroup()) {
|
if (!channel->isMegagroup()) {
|
||||||
if (auto media = getMedia()) {
|
if (const auto media = this->media()) {
|
||||||
if (auto document = media->getDocument()) {
|
if (const auto document = media->document()) {
|
||||||
if (document->isVideoMessage()) {
|
if (document->isVideoMessage()) {
|
||||||
return qsl("https://telesco.pe/") + query;
|
return qsl("https://telesco.pe/") + query;
|
||||||
}
|
}
|
||||||
|
@ -565,13 +528,6 @@ MsgId HistoryItem::idOriginal() const {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryItem::hasOutLayout() const {
|
|
||||||
if (history()->peer->isSelf()) {
|
|
||||||
return !Has<HistoryMessageForwarded>();
|
|
||||||
}
|
|
||||||
return out() && !isPost();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HistoryItem::needCheck() const {
|
bool HistoryItem::needCheck() const {
|
||||||
return out() || (id < 0 && history()->peer->isSelf());
|
return out() || (id < 0 && history()->peer->isSelf());
|
||||||
}
|
}
|
||||||
|
@ -658,84 +614,7 @@ void HistoryItem::setUnreadBarFreezed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageGroupId HistoryItem::groupId() const {
|
MessageGroupId HistoryItem::groupId() const {
|
||||||
if (const auto group = Get<HistoryMessageGroup>()) {
|
return _groupId;
|
||||||
return group->groupId;
|
|
||||||
}
|
|
||||||
return MessageGroupId::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HistoryItem::groupIdValidityChanged() {
|
|
||||||
if (Has<HistoryMessageGroup>()) {
|
|
||||||
if (_media && _media->canBeGrouped()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
RemoveComponents(HistoryMessageGroup::Bit());
|
|
||||||
Auth().data().requestItemViewResize(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryItem::makeGroupMember(not_null<HistoryItem*> leader) {
|
|
||||||
Expects(leader != this);
|
|
||||||
|
|
||||||
const auto group = Get<HistoryMessageGroup>();
|
|
||||||
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<not_null<HistoryItem*>> &&others) {
|
|
||||||
const auto group = Get<HistoryMessageGroup>();
|
|
||||||
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<HistoryMessageGroup>()) {
|
|
||||||
if (group->leader == this) {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
return group->leader->Get<HistoryMessageGroup>();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryItem::resetGroupMedia(
|
|
||||||
const std::vector<not_null<HistoryItem*>> &others) {
|
|
||||||
if (!others.empty()) {
|
|
||||||
_media = std::make_unique<HistoryGroupedMedia>(this, others);
|
|
||||||
} else if (_media) {
|
|
||||||
_media = _media->takeLastFromGroup();
|
|
||||||
}
|
|
||||||
Auth().data().requestItemViewResize(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int HistoryItem::displayedDateHeight() const {
|
int HistoryItem::displayedDateHeight() const {
|
||||||
|
@ -758,79 +637,74 @@ bool HistoryItem::isEmpty() const {
|
||||||
void HistoryItem::clipCallback(Media::Clip::Notification notification) {
|
void HistoryItem::clipCallback(Media::Clip::Notification notification) {
|
||||||
using namespace Media::Clip;
|
using namespace Media::Clip;
|
||||||
|
|
||||||
auto media = getMedia();
|
auto media = this->media();
|
||||||
if (!media) {
|
if (!media) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto reader = media->getClipReader();
|
// #TODO GIFs
|
||||||
if (!reader) {
|
//auto reader = media->getClipReader();
|
||||||
return;
|
//if (!reader) {
|
||||||
}
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
switch (notification) {
|
//switch (notification) {
|
||||||
case NotificationReinit: {
|
//case NotificationReinit: {
|
||||||
auto stopped = false;
|
// auto stopped = false;
|
||||||
if (reader->autoPausedGif()) {
|
// if (reader->autoPausedGif()) {
|
||||||
auto amVisible = false;
|
// auto amVisible = false;
|
||||||
Auth().data().queryItemVisibility().notify({ this, &amVisible }, true);
|
// Auth().data().queryItemVisibility().notify({ this, &amVisible }, true);
|
||||||
if (!amVisible) { // stop animation if it is not visible
|
// if (!amVisible) { // stop animation if it is not visible
|
||||||
media->stopInline();
|
// media->stopInline();
|
||||||
if (auto document = media->getDocument()) { // forget data from memory
|
// if (auto document = media->getDocument()) { // forget data from memory
|
||||||
document->forget();
|
// document->forget();
|
||||||
}
|
// }
|
||||||
stopped = true;
|
// stopped = true;
|
||||||
}
|
// }
|
||||||
} else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) {
|
// } else if (reader->mode() == Media::Clip::Reader::Mode::Video && reader->state() == Media::Clip::State::Finished) {
|
||||||
// Stop finished video message.
|
// // Stop finished video message.
|
||||||
media->stopInline();
|
// media->stopInline();
|
||||||
}
|
// }
|
||||||
if (!stopped) {
|
// if (!stopped) {
|
||||||
Auth().data().requestItemViewResize(this);
|
// Auth().data().requestItemViewResize(this);
|
||||||
Auth().data().markItemLayoutChanged(this);
|
// Auth().data().markItemLayoutChange(this);
|
||||||
Global::RefPendingRepaintItems().insert(this);
|
// Global::RefPendingRepaintItems().insert(this);
|
||||||
}
|
// }
|
||||||
} break;
|
//} break;
|
||||||
|
|
||||||
case NotificationRepaint: {
|
//case NotificationRepaint: {
|
||||||
if (!reader->currentDisplayed()) {
|
// if (!reader->currentDisplayed()) {
|
||||||
Auth().data().requestItemRepaint(this);
|
// Auth().data().requestItemRepaint(this);
|
||||||
}
|
// }
|
||||||
} break;
|
//} break;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryItem::audioTrackUpdated() {
|
void HistoryItem::audioTrackUpdated() {
|
||||||
auto media = getMedia();
|
auto media = this->media();
|
||||||
if (!media) {
|
if (!media) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto reader = media->getClipReader();
|
// #TODO GIFs
|
||||||
if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) {
|
//auto reader = media->getClipReader();
|
||||||
return;
|
//if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) {
|
||||||
}
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
auto audio = reader->audioMsgId();
|
//auto audio = reader->audioMsgId();
|
||||||
auto current = Media::Player::mixer()->currentState(audio.type());
|
//auto current = Media::Player::mixer()->currentState(audio.type());
|
||||||
if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) {
|
//if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) {
|
||||||
media->stopInline();
|
// media->stopInline();
|
||||||
} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) {
|
//} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) {
|
||||||
if (!reader->videoPaused()) {
|
// if (!reader->videoPaused()) {
|
||||||
reader->pauseResumeVideo();
|
// reader->pauseResumeVideo();
|
||||||
}
|
// }
|
||||||
} else {
|
//} else {
|
||||||
if (reader->videoPaused()) {
|
// if (reader->videoPaused()) {
|
||||||
reader->pauseResumeVideo();
|
// reader->pauseResumeVideo();
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
bool HistoryItem::isUnderCursor() const {
|
|
||||||
if (const auto view = App::hoveredItem()) {
|
|
||||||
return view->data() == this;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem *HistoryItem::previousItem() const {
|
HistoryItem *HistoryItem::previousItem() const {
|
||||||
|
@ -869,7 +743,7 @@ QString HistoryItem::notificationText() const {
|
||||||
QString HistoryItem::inDialogsText(DrawInDialog way) const {
|
QString HistoryItem::inDialogsText(DrawInDialog way) const {
|
||||||
auto getText = [this]() {
|
auto getText = [this]() {
|
||||||
if (emptyText()) {
|
if (emptyText()) {
|
||||||
return _media ? _media->inDialogsText() : QString();
|
return _media ? _media->chatsListText() : QString();
|
||||||
}
|
}
|
||||||
return TextUtilities::Clean(_text.originalText());
|
return TextUtilities::Clean(_text.originalText());
|
||||||
};
|
};
|
||||||
|
@ -879,7 +753,7 @@ QString HistoryItem::inDialogsText(DrawInDialog way) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else if (!_history->peer->isUser() || out()) {
|
} else if (!_history->peer->isUser() || out()) {
|
||||||
return author();
|
return author();
|
||||||
} else if (_history->peer->isSelf() && !hasOutLayout()) {
|
} else if (_history->peer->isSelf() && !Has<HistoryMessageForwarded>()) {
|
||||||
return senderOriginal();
|
return senderOriginal();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -914,6 +788,7 @@ void HistoryItem::drawInDialog(
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem::~HistoryItem() {
|
HistoryItem::~HistoryItem() {
|
||||||
|
Auth().data().groups().unregisterMessage(this);
|
||||||
App::historyUnregItem(this);
|
App::historyUnregItem(this);
|
||||||
if (id < 0 && !App::quitting()) {
|
if (id < 0 && !App::quitting()) {
|
||||||
Auth().uploader().cancel(fullId());
|
Auth().uploader().cancel(fullId());
|
||||||
|
@ -940,3 +815,135 @@ ClickHandlerPtr goToMessageClickHandler(
|
||||||
ClickHandlerPtr goToMessageClickHandler(not_null<HistoryItem*> item) {
|
ClickHandlerPtr goToMessageClickHandler(not_null<HistoryItem*> item) {
|
||||||
return goToMessageClickHandler(item->history()->peer, item->id);
|
return goToMessageClickHandler(item->history()->peer, item->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<HistoryItem*> HistoryItem::Create(
|
||||||
|
not_null<History*> 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().");
|
||||||
|
}
|
||||||
|
|
|
@ -14,8 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
|
||||||
enum class UnreadMentionType;
|
enum class UnreadMentionType;
|
||||||
struct MessageGroupId;
|
|
||||||
struct HistoryMessageGroup;
|
|
||||||
struct HistoryMessageReplyMarkup;
|
struct HistoryMessageReplyMarkup;
|
||||||
class ReplyKeyboard;
|
class ReplyKeyboard;
|
||||||
class HistoryMessage;
|
class HistoryMessage;
|
||||||
|
@ -42,6 +40,7 @@ struct RippleAnimation;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
struct MessagePosition;
|
struct MessagePosition;
|
||||||
|
class Media;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
@ -52,21 +51,12 @@ namespace HistoryView {
|
||||||
enum class Context : char;
|
enum class Context : char;
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
||||||
namespace internal {
|
class HistoryItem : public RuntimeComposer<HistoryItem> {
|
||||||
|
|
||||||
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 {
|
|
||||||
public:
|
public:
|
||||||
|
static not_null<HistoryItem*> Create(
|
||||||
|
not_null<History*> history,
|
||||||
|
const MTPMessage &message);
|
||||||
|
|
||||||
virtual void dependencyItemRemoved(HistoryItem *dependency) {
|
virtual void dependencyItemRemoved(HistoryItem *dependency) {
|
||||||
}
|
}
|
||||||
virtual bool updateDependencyItem() {
|
virtual bool updateDependencyItem() {
|
||||||
|
@ -145,19 +135,12 @@ public:
|
||||||
bool isSilent() const {
|
bool isSilent() const {
|
||||||
return _flags & MTPDmessage::Flag::f_silent;
|
return _flags & MTPDmessage::Flag::f_silent;
|
||||||
}
|
}
|
||||||
bool hasOutLayout() const;
|
virtual int viewsCount() const {
|
||||||
virtual int32 viewsCount() const {
|
|
||||||
return hasViews() ? 1 : -1;
|
return hasViews() ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool needCheck() const;
|
virtual bool needCheck() const;
|
||||||
|
|
||||||
[[nodiscard]] virtual TextSelection adjustSelection(
|
|
||||||
TextSelection selection,
|
|
||||||
TextSelectType type) const {
|
|
||||||
return selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool serviceMsg() const {
|
virtual bool serviceMsg() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -173,17 +156,10 @@ public:
|
||||||
virtual void addToUnreadMentions(UnreadMentionType type);
|
virtual void addToUnreadMentions(UnreadMentionType type);
|
||||||
virtual void eraseFromUnreadMentions() {
|
virtual void eraseFromUnreadMentions() {
|
||||||
}
|
}
|
||||||
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0;
|
||||||
|
|
||||||
void indexAsNewItem();
|
void indexAsNewItem();
|
||||||
|
|
||||||
virtual bool hasBubble() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual TextWithEntities selectedText(TextSelection selection) const {
|
|
||||||
return { qsl("[-]"), EntitiesInText() };
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual QString notificationHeader() const {
|
virtual QString notificationHeader() const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
@ -204,29 +180,10 @@ public:
|
||||||
return { QString(), EntitiesInText() };
|
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 setViewsCount(int32 count) {
|
||||||
}
|
}
|
||||||
virtual void setRealId(MsgId newId);
|
virtual void setRealId(MsgId newId);
|
||||||
|
|
||||||
virtual bool displayEditedBadge() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
virtual QDateTime displayedEditDate() const {
|
|
||||||
return QDateTime();
|
|
||||||
}
|
|
||||||
virtual void refreshEditedBadge() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawInDialog(
|
void drawInDialog(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const QRect &r,
|
const QRect &r,
|
||||||
|
@ -242,8 +199,8 @@ public:
|
||||||
|
|
||||||
bool isPinned() const;
|
bool isPinned() const;
|
||||||
bool canPin() const;
|
bool canPin() const;
|
||||||
bool canForward() const;
|
virtual bool allowsForward() const;
|
||||||
bool canEdit(const QDateTime &cur) const;
|
virtual bool allowsEdit(const QDateTime &now) const;
|
||||||
bool canDelete() const;
|
bool canDelete() const;
|
||||||
bool canDeleteForEveryone(const QDateTime &cur) const;
|
bool canDeleteForEveryone(const QDateTime &cur) const;
|
||||||
bool suggestBanReport() const;
|
bool suggestBanReport() const;
|
||||||
|
@ -261,7 +218,7 @@ public:
|
||||||
}
|
}
|
||||||
Data::MessagePosition position() const;
|
Data::MessagePosition position() const;
|
||||||
|
|
||||||
HistoryMedia *getMedia() const {
|
Data::Media *media() const {
|
||||||
return _media.get();
|
return _media.get();
|
||||||
}
|
}
|
||||||
virtual void setText(const TextWithEntities &textWithEntities) {
|
virtual void setText(const TextWithEntities &textWithEntities) {
|
||||||
|
@ -270,29 +227,6 @@ public:
|
||||||
return false;
|
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
|
virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -328,25 +262,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
bool isHiddenByGroup() const {
|
|
||||||
return _flags & MTPDmessage_ClientFlag::f_hidden_by_group;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageGroupId groupId() const;
|
MessageGroupId groupId() const;
|
||||||
bool groupIdValidityChanged();
|
|
||||||
void validateGroupId() {
|
|
||||||
// Just ignore the result.
|
|
||||||
groupIdValidityChanged();
|
|
||||||
}
|
|
||||||
void makeGroupMember(not_null<HistoryItem*> leader);
|
|
||||||
void makeGroupLeader(std::vector<not_null<HistoryItem*>> &&others);
|
|
||||||
HistoryMessageGroup *getFullGroup();
|
|
||||||
|
|
||||||
void clipCallback(Media::Clip::Notification notification);
|
void clipCallback(Media::Clip::Notification notification);
|
||||||
void audioTrackUpdated();
|
void audioTrackUpdated();
|
||||||
|
|
||||||
bool isUnderCursor() const;
|
|
||||||
|
|
||||||
HistoryItem *previousItem() const;
|
HistoryItem *previousItem() const;
|
||||||
HistoryItem *nextItem() const;
|
HistoryItem *nextItem() const;
|
||||||
|
|
||||||
|
@ -388,27 +309,20 @@ protected:
|
||||||
ReplyKeyboard *inlineReplyKeyboard();
|
ReplyKeyboard *inlineReplyKeyboard();
|
||||||
void invalidateChatsListEntry();
|
void invalidateChatsListEntry();
|
||||||
|
|
||||||
[[nodiscard]] TextSelection skipTextSelection(
|
void setGroupId(MessageGroupId groupId);
|
||||||
TextSelection selection) const {
|
|
||||||
return internal::unshiftSelection(selection, _text);
|
|
||||||
}
|
|
||||||
[[nodiscard]] TextSelection unskipTextSelection(
|
|
||||||
TextSelection selection) const {
|
|
||||||
return internal::shiftSelection(selection, _text);
|
|
||||||
}
|
|
||||||
|
|
||||||
Text _text = { int(st::msgMinWidth) };
|
Text _text = { int(st::msgMinWidth) };
|
||||||
int _textWidth = -1;
|
int _textWidth = -1;
|
||||||
int _textHeight = 0;
|
int _textHeight = 0;
|
||||||
|
|
||||||
HistoryMediaPtr _media;
|
std::unique_ptr<Data::Media> _media;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resetGroupMedia(const std::vector<not_null<HistoryItem*>> &others);
|
|
||||||
|
|
||||||
HistoryView::Element *_mainView = nullptr;
|
HistoryView::Element *_mainView = nullptr;
|
||||||
friend class HistoryView::Element;
|
friend class HistoryView::Element;
|
||||||
|
|
||||||
|
MessageGroupId _groupId = MessageGroupId::None;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// make all the constructors in HistoryItem children protected
|
// make all the constructors in HistoryItem children protected
|
||||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/media_audio.h"
|
#include "media/media_audio.h"
|
||||||
#include "media/player/media_player_instance.h"
|
#include "media/player/media_player_instance.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
@ -191,7 +192,7 @@ void HistoryMessageReply::updateName() const {
|
||||||
: App::peerName(replyToMsg->author());
|
: App::peerName(replyToMsg->author());
|
||||||
replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
|
replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
|
||||||
replyToVersion = replyToMsg->author()->nameVersion;
|
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 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
|
||||||
int32 w = replyToName.maxWidth();
|
int32 w = replyToName.maxWidth();
|
||||||
if (replyToVia) {
|
if (replyToVia) {
|
||||||
|
@ -207,7 +208,7 @@ void HistoryMessageReply::updateName() const {
|
||||||
|
|
||||||
void HistoryMessageReply::resize(int width) const {
|
void HistoryMessageReply::resize(int width) const {
|
||||||
if (replyToVia) {
|
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;
|
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);
|
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<const HistoryView::Element*> holder,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int w,
|
||||||
|
PaintFlags flags) const {
|
||||||
bool selected = (flags & PaintFlag::Selected), outbg = holder->hasOutLayout();
|
bool selected = (flags & PaintFlag::Selected), outbg = holder->hasOutLayout();
|
||||||
|
|
||||||
style::color bar = st::msgImgReplyBarColor;
|
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 (w > st::msgReplyBarSkip) {
|
||||||
if (replyToMsg) {
|
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()) {
|
if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) {
|
||||||
hasPreview = false;
|
hasPreview = false;
|
||||||
}
|
}
|
||||||
auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
|
auto previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
|
||||||
|
|
||||||
if (hasPreview) {
|
if (hasPreview) {
|
||||||
ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview();
|
const auto replyPreview = replyToMsg->media()->replyPreview();
|
||||||
if (!replyPreview->isNull()) {
|
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 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();
|
auto previewWidth = replyPreview->width() / cIntRetinaFactor();
|
||||||
|
@ -834,11 +841,14 @@ void HistoryMessageDate::paint(Painter &p, int y, int w) const {
|
||||||
|
|
||||||
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default;
|
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) {
|
HistoryMessageLogEntryOriginal &HistoryMessageLogEntryOriginal::operator=(
|
||||||
_page = std::move(other._page);
|
HistoryMessageLogEntryOriginal &&other) {
|
||||||
|
page = std::move(other.page);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,38 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "base/value_ordering.h"
|
|
||||||
|
|
||||||
class HistoryDocument;
|
class HistoryDocument;
|
||||||
class HistoryWebPage;
|
class WebPageData;
|
||||||
|
|
||||||
struct MessageGroupId {
|
namespace HistoryView {
|
||||||
using Underlying = uint64;
|
class Element;
|
||||||
|
} // namespace HistoryView
|
||||||
|
|
||||||
enum Type : Underlying {
|
struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia, HistoryItem> {
|
||||||
None = 0,
|
|
||||||
} value;
|
|
||||||
|
|
||||||
MessageGroupId(Type value = None) : value(value) {
|
|
||||||
}
|
|
||||||
static MessageGroupId FromRaw(Underlying value) {
|
|
||||||
return static_cast<Type>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
|
||||||
return value != None;
|
|
||||||
}
|
|
||||||
Underlying raw() const {
|
|
||||||
return static_cast<Underlying>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend inline Type value_ordering_helper(MessageGroupId value) {
|
|
||||||
return value.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia> {
|
|
||||||
void create(UserId userId);
|
void create(UserId userId);
|
||||||
void resize(int32 availw) const;
|
void resize(int32 availw) const;
|
||||||
|
|
||||||
|
@ -50,13 +27,13 @@ struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia> {
|
||||||
ClickHandlerPtr link;
|
ClickHandlerPtr link;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews> {
|
struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews, HistoryItem> {
|
||||||
QString _viewsText;
|
QString _viewsText;
|
||||||
int _views = 0;
|
int _views = 0;
|
||||||
int _viewsWidth = 0;
|
int _viewsWidth = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned> {
|
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned, HistoryItem> {
|
||||||
void refresh(const QString &date);
|
void refresh(const QString &date);
|
||||||
int maxWidth() const;
|
int maxWidth() const;
|
||||||
|
|
||||||
|
@ -64,7 +41,7 @@ struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned> {
|
||||||
Text signature;
|
Text signature;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited> {
|
struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, HistoryItem> {
|
||||||
void refresh(const QString &date, bool displayed);
|
void refresh(const QString &date, bool displayed);
|
||||||
int maxWidth() const;
|
int maxWidth() const;
|
||||||
|
|
||||||
|
@ -72,7 +49,7 @@ struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited> {
|
||||||
Text text;
|
Text text;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded> {
|
struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded, HistoryItem> {
|
||||||
void create(const HistoryMessageVia *via) const;
|
void create(const HistoryMessageVia *via) const;
|
||||||
|
|
||||||
QDateTime originalDate;
|
QDateTime originalDate;
|
||||||
|
@ -85,7 +62,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
|
||||||
MsgId savedFromMsgId = 0;
|
MsgId savedFromMsgId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply> {
|
struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> {
|
||||||
HistoryMessageReply() = default;
|
HistoryMessageReply() = default;
|
||||||
HistoryMessageReply(const HistoryMessageReply &other) = delete;
|
HistoryMessageReply(const HistoryMessageReply &other) = delete;
|
||||||
HistoryMessageReply(HistoryMessageReply &&other) = delete;
|
HistoryMessageReply(HistoryMessageReply &&other) = delete;
|
||||||
|
@ -125,7 +102,7 @@ struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply> {
|
||||||
friend inline constexpr auto is_flag_type(PaintFlag) { return true; };
|
friend inline constexpr auto is_flag_type(PaintFlag) { return true; };
|
||||||
void paint(
|
void paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const HistoryItem *holder,
|
not_null<const HistoryView::Element*> holder,
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
int w,
|
int w,
|
||||||
|
@ -171,7 +148,7 @@ struct HistoryMessageMarkupButton {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageReplyMarkup : public RuntimeComponent<HistoryMessageReplyMarkup> {
|
struct HistoryMessageReplyMarkup : public RuntimeComponent<HistoryMessageReplyMarkup, HistoryItem> {
|
||||||
using Button = HistoryMessageMarkupButton;
|
using Button = HistoryMessageMarkupButton;
|
||||||
|
|
||||||
HistoryMessageReplyMarkup() = default;
|
HistoryMessageReplyMarkup() = default;
|
||||||
|
@ -348,7 +325,7 @@ private:
|
||||||
|
|
||||||
// Any HistoryItem can have this Component for
|
// Any HistoryItem can have this Component for
|
||||||
// displaying the day mark above the message.
|
// displaying the day mark above the message.
|
||||||
struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate> {
|
struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate, HistoryItem> {
|
||||||
void init(const QDateTime &date);
|
void init(const QDateTime &date);
|
||||||
|
|
||||||
int height() const;
|
int height() const;
|
||||||
|
@ -360,7 +337,7 @@ struct HistoryMessageDate : public RuntimeComponent<HistoryMessageDate> {
|
||||||
|
|
||||||
// Any HistoryItem can have this Component for
|
// Any HistoryItem can have this Component for
|
||||||
// displaying the unread messages bar above the message.
|
// displaying the unread messages bar above the message.
|
||||||
struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar> {
|
struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar, HistoryItem> {
|
||||||
void init(int count);
|
void init(int count);
|
||||||
|
|
||||||
static int height();
|
static int height();
|
||||||
|
@ -381,25 +358,20 @@ struct HistoryMessageUnreadBar : public RuntimeComponent<HistoryMessageUnreadBar
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageGroup : public RuntimeComponent<HistoryMessageGroup> {
|
|
||||||
MessageGroupId groupId = MessageGroupId::None;
|
|
||||||
HistoryItem *leader = nullptr;
|
|
||||||
std::vector<not_null<HistoryItem*>> others;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Special type of Component for the channel actions log.
|
// Special type of Component for the channel actions log.
|
||||||
struct HistoryMessageLogEntryOriginal : public RuntimeComponent<HistoryMessageLogEntryOriginal> {
|
struct HistoryMessageLogEntryOriginal
|
||||||
|
: public RuntimeComponent<HistoryMessageLogEntryOriginal, HistoryItem> {
|
||||||
HistoryMessageLogEntryOriginal();
|
HistoryMessageLogEntryOriginal();
|
||||||
HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other);
|
HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other);
|
||||||
HistoryMessageLogEntryOriginal &operator=(HistoryMessageLogEntryOriginal &&other);
|
HistoryMessageLogEntryOriginal &operator=(HistoryMessageLogEntryOriginal &&other);
|
||||||
~HistoryMessageLogEntryOriginal();
|
~HistoryMessageLogEntryOriginal();
|
||||||
|
|
||||||
std::unique_ptr<HistoryWebPage> _page;
|
WebPageData *page = nullptr;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileClickHandler;
|
class FileClickHandler;
|
||||||
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed> {
|
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed, HistoryDocument> {
|
||||||
std::shared_ptr<FileClickHandler> _linksavel, _linkcancell;
|
std::shared_ptr<FileClickHandler> _linksavel, _linkcancell;
|
||||||
int _thumbw = 0;
|
int _thumbw = 0;
|
||||||
|
|
||||||
|
@ -407,13 +379,13 @@ struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed>
|
||||||
mutable QString _link;
|
mutable QString _link;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned> {
|
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned, HistoryDocument> {
|
||||||
HistoryDocumentCaptioned();
|
HistoryDocumentCaptioned();
|
||||||
|
|
||||||
Text _caption;
|
Text _caption;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed> {
|
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed, HistoryDocument> {
|
||||||
QString _name;
|
QString _name;
|
||||||
int _namew = 0;
|
int _namew = 0;
|
||||||
};
|
};
|
||||||
|
@ -426,7 +398,7 @@ struct HistoryDocumentVoicePlayback {
|
||||||
BasicAnimation _a_progress;
|
BasicAnimation _a_progress;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HistoryDocumentVoice : public RuntimeComponent<HistoryDocumentVoice> {
|
class HistoryDocumentVoice : public RuntimeComponent<HistoryDocumentVoice, HistoryDocument> {
|
||||||
// We don't use float64 because components should align to pointer even on 32bit systems.
|
// We don't use float64 because components should align to pointer even on 32bit systems.
|
||||||
static constexpr float64 kFloatToIntMultiplier = 65536.;
|
static constexpr float64 kFloatToIntMultiplier = 65536.;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_media.h"
|
#include "history/history_media.h"
|
||||||
|
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const {
|
Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const {
|
||||||
|
@ -23,11 +24,15 @@ QSize HistoryMedia::countCurrentSize(int newWidth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TextSelection HistoryMedia::skipSelection(TextSelection selection) const {
|
TextSelection HistoryMedia::skipSelection(TextSelection selection) const {
|
||||||
return internal::unshiftSelection(selection, fullSelectionLength());
|
return HistoryView::UnshiftItemSelection(
|
||||||
|
selection,
|
||||||
|
fullSelectionLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
TextSelection HistoryMedia::unskipSelection(TextSelection selection) const {
|
TextSelection HistoryMedia::unskipSelection(TextSelection selection) const {
|
||||||
return internal::shiftSelection(selection, fullSelectionLength());
|
return HistoryView::ShiftItemSelection(
|
||||||
|
selection,
|
||||||
|
fullSelectionLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryTextState HistoryMedia::getStateGrouped(
|
HistoryTextState HistoryMedia::getStateGrouped(
|
||||||
|
|
|
@ -52,21 +52,18 @@ enum HistoryMediaType : char {
|
||||||
|
|
||||||
class HistoryMedia : public HistoryView::Object {
|
class HistoryMedia : public HistoryView::Object {
|
||||||
public:
|
public:
|
||||||
HistoryMedia(not_null<HistoryItem*> parent) : _parent(parent) {
|
using Element = HistoryView::Element;
|
||||||
|
|
||||||
|
HistoryMedia(not_null<Element*> parent) : _parent(parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual HistoryMediaType type() const = 0;
|
virtual HistoryMediaType type() const = 0;
|
||||||
|
virtual std::unique_ptr<HistoryMedia> clone(
|
||||||
virtual QString notificationText() const {
|
not_null<Element*> newParent,
|
||||||
return QString();
|
not_null<Element*> 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;
|
virtual TextWithEntities selectedText(TextSelection selection) const = 0;
|
||||||
|
|
||||||
bool hasPoint(QPoint point) const {
|
bool hasPoint(QPoint point) const {
|
||||||
|
@ -85,7 +82,7 @@ public:
|
||||||
virtual bool allowsFastShare() const {
|
virtual bool allowsFastShare() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
virtual void refreshParentId(not_null<HistoryItem*> realParent) {
|
virtual void refreshParentId(not_null<Element*> realParent) {
|
||||||
}
|
}
|
||||||
virtual void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const = 0;
|
virtual void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const = 0;
|
||||||
virtual HistoryTextState getState(QPoint point, HistoryStateRequest request) const = 0;
|
virtual HistoryTextState getState(QPoint point, HistoryStateRequest request) const = 0;
|
||||||
|
@ -109,10 +106,6 @@ public:
|
||||||
TextSelectType type) const {
|
TextSelectType type) const {
|
||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
[[nodiscard]] virtual bool consumeMessageText(
|
|
||||||
const TextWithEntities &textWithEntities) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
[[nodiscard]] virtual uint16 fullSelectionLength() const {
|
[[nodiscard]] virtual uint16 fullSelectionLength() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -132,9 +125,6 @@ public:
|
||||||
virtual bool uploading() const {
|
virtual bool uploading() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
virtual std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const = 0;
|
|
||||||
|
|
||||||
virtual PhotoData *getPhoto() const {
|
virtual PhotoData *getPhoto() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -187,20 +177,10 @@ public:
|
||||||
virtual std::unique_ptr<HistoryMedia> takeLastFromGroup() {
|
virtual std::unique_ptr<HistoryMedia> takeLastFromGroup() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
virtual bool applyGroup(
|
virtual bool applyGroup(const std::vector<not_null<Element*>> &others) {
|
||||||
const std::vector<not_null<HistoryItem*>> &others) {
|
|
||||||
return others.empty();
|
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 {
|
virtual bool animating() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -253,10 +233,6 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool canEditCaption() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sometimes click on media in message is overloaded by the message:
|
// 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)
|
// (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
|
// But the overloading click handler should be used only when media
|
||||||
|
@ -271,7 +247,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
not_null<HistoryItem*> _parent;
|
not_null<Element*> _parent;
|
||||||
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
|
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,13 +18,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
|
|
||||||
HistoryGroupedMedia::Element::Element(not_null<HistoryItem*> item)
|
HistoryGroupedMedia::Part::Part(not_null<Element*> view)
|
||||||
: item(item) {
|
: view(view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryGroupedMedia::HistoryGroupedMedia(
|
HistoryGroupedMedia::HistoryGroupedMedia(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
const std::vector<not_null<HistoryItem*>> &others)
|
const std::vector<not_null<Element*>> &others)
|
||||||
: HistoryMedia(parent)
|
: HistoryMedia(parent)
|
||||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||||
const auto result = applyGroup(others);
|
const auto result = applyGroup(others);
|
||||||
|
@ -32,23 +32,17 @@ HistoryGroupedMedia::HistoryGroupedMedia(
|
||||||
Ensures(result);
|
Ensures(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<HistoryMedia> HistoryGroupedMedia::clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const {
|
|
||||||
return main()->clone(newParent, realParent);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize HistoryGroupedMedia::countOptimalSize() {
|
QSize HistoryGroupedMedia::countOptimalSize() {
|
||||||
if (_caption.hasSkipBlock()) {
|
if (_caption.hasSkipBlock()) {
|
||||||
_caption.setSkipBlock(
|
_caption.updateSkipBlock(
|
||||||
_parent->skipBlockWidth(),
|
_parent->skipBlockWidth(),
|
||||||
_parent->skipBlockHeight());
|
_parent->skipBlockHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<QSize> sizes;
|
std::vector<QSize> sizes;
|
||||||
sizes.reserve(_elements.size());
|
sizes.reserve(_parts.size());
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
const auto &media = element.content;
|
const auto &media = part.content;
|
||||||
media->initDimensions();
|
media->initDimensions();
|
||||||
sizes.push_back(media->sizeForGrouping());
|
sizes.push_back(media->sizeForGrouping());
|
||||||
}
|
}
|
||||||
|
@ -58,7 +52,7 @@ QSize HistoryGroupedMedia::countOptimalSize() {
|
||||||
st::historyGroupWidthMax,
|
st::historyGroupWidthMax,
|
||||||
st::historyGroupWidthMin,
|
st::historyGroupWidthMin,
|
||||||
st::historyGroupSkip);
|
st::historyGroupSkip);
|
||||||
Assert(layout.size() == _elements.size());
|
Assert(layout.size() == _parts.size());
|
||||||
|
|
||||||
auto maxWidth = 0;
|
auto maxWidth = 0;
|
||||||
auto minHeight = 0;
|
auto minHeight = 0;
|
||||||
|
@ -66,8 +60,8 @@ QSize HistoryGroupedMedia::countOptimalSize() {
|
||||||
const auto &item = layout[i];
|
const auto &item = layout[i];
|
||||||
accumulate_max(maxWidth, item.geometry.x() + item.geometry.width());
|
accumulate_max(maxWidth, item.geometry.x() + item.geometry.width());
|
||||||
accumulate_max(minHeight, item.geometry.y() + item.geometry.height());
|
accumulate_max(minHeight, item.geometry.y() + item.geometry.height());
|
||||||
_elements[i].initialGeometry = item.geometry;
|
_parts[i].initialGeometry = item.geometry;
|
||||||
_elements[i].sides = item.sides;
|
_parts[i].sides = item.sides;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()) {
|
||||||
|
@ -93,9 +87,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
|
||||||
return int(std::round(value * factor));
|
return int(std::round(value * factor));
|
||||||
};
|
};
|
||||||
const auto spacing = scale(initialSpacing);
|
const auto spacing = scale(initialSpacing);
|
||||||
for (auto &element : _elements) {
|
for (auto &part : _parts) {
|
||||||
const auto sides = element.sides;
|
const auto sides = part.sides;
|
||||||
const auto initialGeometry = element.initialGeometry;
|
const auto initialGeometry = part.initialGeometry;
|
||||||
const auto needRightSkip = !(sides & RectPart::Right);
|
const auto needRightSkip = !(sides & RectPart::Right);
|
||||||
const auto needBottomSkip = !(sides & RectPart::Bottom);
|
const auto needBottomSkip = !(sides & RectPart::Bottom);
|
||||||
const auto initialLeft = initialGeometry.x();
|
const auto initialLeft = initialGeometry.x();
|
||||||
|
@ -114,7 +108,7 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
|
||||||
const auto height = scale(initialBottom)
|
const auto height = scale(initialBottom)
|
||||||
- top
|
- top
|
||||||
- (needBottomSkip ? spacing : 0);
|
- (needBottomSkip ? spacing : 0);
|
||||||
element.geometry = QRect(left, top, width, height);
|
part.geometry = QRect(left, top, width, height);
|
||||||
|
|
||||||
accumulate_max(newHeight, top + height);
|
accumulate_max(newHeight, top + height);
|
||||||
}
|
}
|
||||||
|
@ -130,10 +124,9 @@ QSize HistoryGroupedMedia::countCurrentSize(int newWidth) {
|
||||||
return { newWidth, newHeight };
|
return { newWidth, newHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryGroupedMedia::refreshParentId(
|
void HistoryGroupedMedia::refreshParentId(not_null<Element*> realParent) {
|
||||||
not_null<HistoryItem*> realParent) {
|
for (const auto &part : _parts) {
|
||||||
for (const auto &element : _elements) {
|
part.content->refreshParentId(part.view);
|
||||||
element.content->refreshParentId(element.item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,29 +135,29 @@ void HistoryGroupedMedia::draw(
|
||||||
const QRect &clip,
|
const QRect &clip,
|
||||||
TextSelection selection,
|
TextSelection selection,
|
||||||
TimeMs ms) const {
|
TimeMs ms) const {
|
||||||
for (auto i = 0, count = int(_elements.size()); i != count; ++i) {
|
for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
|
||||||
const auto &element = _elements[i];
|
const auto &part = _parts[i];
|
||||||
const auto elementSelection = (selection == FullSelection)
|
const auto partSelection = (selection == FullSelection)
|
||||||
? FullSelection
|
? FullSelection
|
||||||
: IsGroupItemSelection(selection, i)
|
: IsGroupItemSelection(selection, i)
|
||||||
? FullSelection
|
? FullSelection
|
||||||
: TextSelection();
|
: TextSelection();
|
||||||
auto corners = Ui::GetCornersFromSides(element.sides);
|
auto corners = Ui::GetCornersFromSides(part.sides);
|
||||||
if (!isBubbleTop()) {
|
if (!isBubbleTop()) {
|
||||||
corners &= ~(RectPart::TopLeft | RectPart::TopRight);
|
corners &= ~(RectPart::TopLeft | RectPart::TopRight);
|
||||||
}
|
}
|
||||||
if (!isBubbleBottom() || !_caption.isEmpty()) {
|
if (!isBubbleBottom() || !_caption.isEmpty()) {
|
||||||
corners &= ~(RectPart::BottomLeft | RectPart::BottomRight);
|
corners &= ~(RectPart::BottomLeft | RectPart::BottomRight);
|
||||||
}
|
}
|
||||||
element.content->drawGrouped(
|
part.content->drawGrouped(
|
||||||
p,
|
p,
|
||||||
clip,
|
clip,
|
||||||
elementSelection,
|
partSelection,
|
||||||
ms,
|
ms,
|
||||||
element.geometry,
|
part.geometry,
|
||||||
corners,
|
corners,
|
||||||
&element.cacheKey,
|
&part.cacheKey,
|
||||||
&element.cache);
|
&part.cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
// date
|
// date
|
||||||
|
@ -177,7 +170,7 @@ void HistoryGroupedMedia::draw(
|
||||||
- _caption.countHeight(captionw);
|
- _caption.countHeight(captionw);
|
||||||
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
|
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);
|
_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 fullRight = width();
|
||||||
auto fullBottom = height();
|
auto fullBottom = height();
|
||||||
if (needInfoDisplay()) {
|
if (needInfoDisplay()) {
|
||||||
|
@ -191,38 +184,38 @@ void HistoryGroupedMedia::draw(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryTextState HistoryGroupedMedia::getElementState(
|
HistoryTextState HistoryGroupedMedia::getPartState(
|
||||||
QPoint point,
|
QPoint point,
|
||||||
HistoryStateRequest request) const {
|
HistoryStateRequest request) const {
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
if (element.geometry.contains(point)) {
|
if (part.geometry.contains(point)) {
|
||||||
auto result = element.content->getStateGrouped(
|
auto result = part.content->getStateGrouped(
|
||||||
element.geometry,
|
part.geometry,
|
||||||
point,
|
point,
|
||||||
request);
|
request);
|
||||||
result.itemId = element.item->fullId();
|
result.itemId = part.view->data()->fullId();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return HistoryTextState(_parent);
|
return HistoryTextState(_parent->data());
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryTextState HistoryGroupedMedia::getState(
|
HistoryTextState HistoryGroupedMedia::getState(
|
||||||
QPoint point,
|
QPoint point,
|
||||||
HistoryStateRequest request) const {
|
HistoryStateRequest request) const {
|
||||||
auto result = getElementState(point, request);
|
auto result = getPartState(point, request);
|
||||||
if (!result.link && !_caption.isEmpty()) {
|
if (!result.link && !_caption.isEmpty()) {
|
||||||
const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right();
|
const auto captionw = width() - st::msgPadding.left() - st::msgPadding.right();
|
||||||
const auto captiony = height()
|
const auto captiony = height()
|
||||||
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
|
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
|
||||||
- _caption.countHeight(captionw);
|
- _caption.countHeight(captionw);
|
||||||
if (QRect(st::msgPadding.left(), captiony, captionw, height() - captiony).contains(point)) {
|
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),
|
point - QPoint(st::msgPadding.left(), captiony),
|
||||||
captionw,
|
captionw,
|
||||||
request.forText()));
|
request.forText()));
|
||||||
}
|
}
|
||||||
} else if (_parent->getMedia() == this) {
|
} else if (_parent->media() == this) {
|
||||||
auto fullRight = width();
|
auto fullRight = width();
|
||||||
auto fullBottom = height();
|
auto fullBottom = height();
|
||||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
|
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
|
||||||
|
@ -241,8 +234,8 @@ HistoryTextState HistoryGroupedMedia::getState(
|
||||||
|
|
||||||
bool HistoryGroupedMedia::toggleSelectionByHandlerClick(
|
bool HistoryGroupedMedia::toggleSelectionByHandlerClick(
|
||||||
const ClickHandlerPtr &p) const {
|
const ClickHandlerPtr &p) const {
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
if (element.content->toggleSelectionByHandlerClick(p)) {
|
if (part.content->toggleSelectionByHandlerClick(p)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,8 +243,8 @@ bool HistoryGroupedMedia::toggleSelectionByHandlerClick(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGroupedMedia::dragItemByHandler(const ClickHandlerPtr &p) const {
|
bool HistoryGroupedMedia::dragItemByHandler(const ClickHandlerPtr &p) const {
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
if (element.content->dragItemByHandler(p)) {
|
if (part.content->dragItemByHandler(p)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,14 +257,6 @@ TextSelection HistoryGroupedMedia::adjustSelection(
|
||||||
return _caption.adjustSelection(selection, type);
|
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(
|
TextWithEntities HistoryGroupedMedia::selectedText(
|
||||||
TextSelection selection) const {
|
TextSelection selection) const {
|
||||||
if (!IsSubGroupSelection(selection)) {
|
if (!IsSubGroupSelection(selection)) {
|
||||||
|
@ -279,7 +264,7 @@ TextWithEntities HistoryGroupedMedia::selectedText(
|
||||||
lang(lng_in_dlg_album),
|
lang(lng_in_dlg_album),
|
||||||
_caption,
|
_caption,
|
||||||
selection);
|
selection);
|
||||||
} else if (IsGroupItemSelection(selection, int(_elements.size()) - 1)) {
|
} else if (IsGroupItemSelection(selection, int(_parts.size()) - 1)) {
|
||||||
return main()->selectedText(FullSelection);
|
return main()->selectedText(FullSelection);
|
||||||
}
|
}
|
||||||
return TextWithEntities();
|
return TextWithEntities();
|
||||||
|
@ -288,78 +273,77 @@ TextWithEntities HistoryGroupedMedia::selectedText(
|
||||||
void HistoryGroupedMedia::clickHandlerActiveChanged(
|
void HistoryGroupedMedia::clickHandlerActiveChanged(
|
||||||
const ClickHandlerPtr &p,
|
const ClickHandlerPtr &p,
|
||||||
bool active) {
|
bool active) {
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
element.content->clickHandlerActiveChanged(p, active);
|
part.content->clickHandlerActiveChanged(p, active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryGroupedMedia::clickHandlerPressedChanged(
|
void HistoryGroupedMedia::clickHandlerPressedChanged(
|
||||||
const ClickHandlerPtr &p,
|
const ClickHandlerPtr &p,
|
||||||
bool pressed) {
|
bool pressed) {
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
element.content->clickHandlerPressedChanged(p, pressed);
|
part.content->clickHandlerPressedChanged(p, pressed);
|
||||||
if (pressed && element.content->dragItemByHandler(p)) {
|
if (pressed && part.content->dragItemByHandler(p)) {
|
||||||
// #TODO pressedLinkItem
|
App::pressedLinkItem(part.view);
|
||||||
//App::pressedLinkItem(element.item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryGroupedMedia::attachToParent() {
|
void HistoryGroupedMedia::attachToParent() {
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
element.content->attachToParent();
|
part.content->attachToParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryGroupedMedia::detachFromParent() {
|
void HistoryGroupedMedia::detachFromParent() {
|
||||||
for (const auto &element : _elements) {
|
for (const auto &part : _parts) {
|
||||||
if (element.content) {
|
if (part.content) {
|
||||||
element.content->detachFromParent();
|
part.content->detachFromParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<HistoryMedia> HistoryGroupedMedia::takeLastFromGroup() {
|
std::unique_ptr<HistoryMedia> HistoryGroupedMedia::takeLastFromGroup() {
|
||||||
return std::move(_elements.back().content);
|
return std::move(_parts.back().content);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGroupedMedia::applyGroup(
|
bool HistoryGroupedMedia::applyGroup(
|
||||||
const std::vector<not_null<HistoryItem*>> &others) {
|
const std::vector<not_null<Element*>> &others) {
|
||||||
if (others.empty()) {
|
if (others.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto pushElement = [&](not_null<HistoryItem*> item) {
|
const auto pushElement = [&](not_null<Element*> view) {
|
||||||
const auto media = item->getMedia();
|
const auto media = view->media();
|
||||||
Assert(media != nullptr && media->canBeGrouped());
|
Assert(media != nullptr && media->canBeGrouped());
|
||||||
|
|
||||||
_elements.push_back(Element(item));
|
_parts.push_back(Part(view));
|
||||||
_elements.back().content = item->getMedia()->clone(_parent, item);
|
_parts.back().content = media->clone(_parent, view);
|
||||||
};
|
};
|
||||||
if (_elements.empty()) {
|
if (_parts.empty()) {
|
||||||
pushElement(_parent);
|
pushElement(_parent);
|
||||||
} else if (validateGroupElements(others)) {
|
} else if (validateGroupParts(others)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're updating other elements, so we just need to preserve the main.
|
// We're updating other elements, so we just need to preserve the main.
|
||||||
auto mainElement = std::move(_elements.back());
|
auto mainPart = std::move(_parts.back());
|
||||||
_elements.erase(_elements.begin(), _elements.end());
|
_parts.erase(_parts.begin(), _parts.end());
|
||||||
_elements.reserve(others.size() + 1);
|
_parts.reserve(others.size() + 1);
|
||||||
for (const auto item : others) {
|
for (const auto view : others) {
|
||||||
pushElement(item);
|
pushElement(view);
|
||||||
}
|
}
|
||||||
_elements.push_back(std::move(mainElement));
|
_parts.push_back(std::move(mainPart));
|
||||||
//_parent->setPendingInitDimensions(); // #TODO group view
|
//_parent->setPendingInitDimensions(); // #TODO group view
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGroupedMedia::validateGroupElements(
|
bool HistoryGroupedMedia::validateGroupParts(
|
||||||
const std::vector<not_null<HistoryItem*>> &others) const {
|
const std::vector<not_null<Element*>> &others) const {
|
||||||
if (_elements.size() != others.size() + 1) {
|
if (_parts.size() != others.size() + 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (auto i = 0, count = int(others.size()); i != count; ++i) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,9 +351,9 @@ bool HistoryGroupedMedia::validateGroupElements(
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<HistoryMedia*> HistoryGroupedMedia::main() const {
|
not_null<HistoryMedia*> HistoryGroupedMedia::main() const {
|
||||||
Expects(!_elements.empty());
|
Expects(!_parts.empty());
|
||||||
|
|
||||||
return _elements.back().content.get();
|
return _parts.back().content.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGroupedMedia::hasReplyPreview() const {
|
bool HistoryGroupedMedia::hasReplyPreview() const {
|
||||||
|
@ -388,15 +372,6 @@ Storage::SharedMediaTypesMask HistoryGroupedMedia::sharedMediaTypes() const {
|
||||||
return main()->sharedMediaTypes();
|
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 {
|
PhotoData *HistoryGroupedMedia::getPhoto() const {
|
||||||
return main()->getPhoto();
|
return main()->getPhoto();
|
||||||
}
|
}
|
||||||
|
@ -407,25 +382,25 @@ DocumentData *HistoryGroupedMedia::getDocument() const {
|
||||||
|
|
||||||
HistoryMessageEdited *HistoryGroupedMedia::displayedEditBadge() const {
|
HistoryMessageEdited *HistoryGroupedMedia::displayedEditBadge() const {
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()) {
|
||||||
return _elements.front().item->Get<HistoryMessageEdited>();
|
return _parts.front().view->data()->Get<HistoryMessageEdited>();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryGroupedMedia::updateNeedBubbleState() {
|
void HistoryGroupedMedia::updateNeedBubbleState() {
|
||||||
const auto getItemCaption = [](const Element &element) {
|
const auto getItemCaption = [](const Part &part) {
|
||||||
if (const auto media = element.item->getMedia()) {
|
if (const auto media = part.view->media()) {
|
||||||
return media->getCaption();
|
return media->getCaption();
|
||||||
}
|
}
|
||||||
return element.content->getCaption();
|
return part.content->getCaption();
|
||||||
};
|
};
|
||||||
const auto captionText = [&] {
|
const auto captionText = [&] {
|
||||||
auto result = getItemCaption(_elements.front());
|
auto result = getItemCaption(_parts.front());
|
||||||
if (result.text.isEmpty()) {
|
if (result.text.isEmpty()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
for (auto i = 1, count = int(_elements.size()); i != count; ++i) {
|
for (auto i = 1, count = int(_parts.size()); i != count; ++i) {
|
||||||
if (!getItemCaption(_elements[i]).text.isEmpty()) {
|
if (!getItemCaption(_parts[i]).text.isEmpty()) {
|
||||||
return TextWithEntities();
|
return TextWithEntities();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,7 +409,7 @@ void HistoryGroupedMedia::updateNeedBubbleState() {
|
||||||
_caption.setText(
|
_caption.setText(
|
||||||
st::messageTextStyle,
|
st::messageTextStyle,
|
||||||
captionText.text + _parent->skipBlock(),
|
captionText.text + _parent->skipBlock(),
|
||||||
Ui::ItemTextNoMonoOptions(_parent));
|
Ui::ItemTextNoMonoOptions(_parent->data()));
|
||||||
_needBubble = computeNeedBubble();
|
_needBubble = computeNeedBubble();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,19 +417,15 @@ bool HistoryGroupedMedia::needsBubble() const {
|
||||||
return _needBubble;
|
return _needBubble;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGroupedMedia::canEditCaption() const {
|
|
||||||
return main()->canEditCaption();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HistoryGroupedMedia::computeNeedBubble() const {
|
bool HistoryGroupedMedia::computeNeedBubble() const {
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (const auto message = _parent->toHistoryMessage()) {
|
if (const auto item = _parent->data()) {
|
||||||
if (message->viaBot()
|
if (item->viaBot()
|
||||||
|| message->Has<HistoryMessageReply>()
|
|| item->Has<HistoryMessageReply>()
|
||||||
|| message->displayForwardedFrom()
|
|| _parent->displayForwardedFrom()
|
||||||
// || message->displayFromName() // #TODO media views
|
|| _parent->displayFromName()
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -463,5 +434,5 @@ bool HistoryGroupedMedia::computeNeedBubble() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryGroupedMedia::needInfoDisplay() const {
|
bool HistoryGroupedMedia::needInfoDisplay() const {
|
||||||
return (_parent->id < 0 || _parent->isUnderCursor());
|
return (_parent->data()->id < 0 || _parent->isUnderCursor());
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,19 +14,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
class HistoryGroupedMedia : public HistoryMedia {
|
class HistoryGroupedMedia : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistoryGroupedMedia(
|
HistoryGroupedMedia(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
const std::vector<not_null<HistoryItem*>> &others);
|
const std::vector<not_null<Element*>> &others);
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeGrouped;
|
return MediaTypeGrouped;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override;
|
|
||||||
|
|
||||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
void refreshParentId(not_null<Element*> realParent) override;
|
||||||
void updateSentMedia(const MTPMessageMedia &media) override;
|
|
||||||
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
|
|
||||||
|
|
||||||
void draw(
|
void draw(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
|
@ -54,8 +49,6 @@ public:
|
||||||
PhotoData *getPhoto() const override;
|
PhotoData *getPhoto() const override;
|
||||||
DocumentData *getDocument() const override;
|
DocumentData *getDocument() const override;
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
QString inDialogsText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
void clickHandlerActiveChanged(
|
void clickHandlerActiveChanged(
|
||||||
|
@ -69,7 +62,7 @@ public:
|
||||||
void detachFromParent() override;
|
void detachFromParent() override;
|
||||||
std::unique_ptr<HistoryMedia> takeLastFromGroup() override;
|
std::unique_ptr<HistoryMedia> takeLastFromGroup() override;
|
||||||
bool applyGroup(
|
bool applyGroup(
|
||||||
const std::vector<not_null<HistoryItem*>> &others) override;
|
const std::vector<not_null<Element*>> &others) override;
|
||||||
|
|
||||||
bool hasReplyPreview() const override;
|
bool hasReplyPreview() const override;
|
||||||
ImagePtr replyPreview() override;
|
ImagePtr replyPreview() override;
|
||||||
|
@ -93,16 +86,15 @@ public:
|
||||||
bool customInfoLayout() const override {
|
bool customInfoLayout() const override {
|
||||||
return _caption.isEmpty();
|
return _caption.isEmpty();
|
||||||
}
|
}
|
||||||
bool canEditCaption() const override;
|
|
||||||
bool allowsFastShare() const override {
|
bool allowsFastShare() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Element {
|
struct Part {
|
||||||
Element(not_null<HistoryItem*> item);
|
Part(not_null<Element*> view);
|
||||||
|
|
||||||
not_null<HistoryItem*> item;
|
not_null<Element*> view;
|
||||||
std::unique_ptr<HistoryMedia> content;
|
std::unique_ptr<HistoryMedia> content;
|
||||||
|
|
||||||
RectParts sides = RectPart::None;
|
RectParts sides = RectPart::None;
|
||||||
|
@ -119,14 +111,14 @@ private:
|
||||||
bool needInfoDisplay() const;
|
bool needInfoDisplay() const;
|
||||||
bool computeNeedBubble() const;
|
bool computeNeedBubble() const;
|
||||||
not_null<HistoryMedia*> main() const;
|
not_null<HistoryMedia*> main() const;
|
||||||
bool validateGroupElements(
|
bool validateGroupParts(
|
||||||
const std::vector<not_null<HistoryItem*>> &others) const;
|
const std::vector<not_null<Element*>> &others) const;
|
||||||
HistoryTextState getElementState(
|
HistoryTextState getPartState(
|
||||||
QPoint point,
|
QPoint point,
|
||||||
HistoryStateRequest request) const;
|
HistoryStateRequest request) const;
|
||||||
|
|
||||||
Text _caption;
|
Text _caption;
|
||||||
std::vector<Element> _elements;
|
std::vector<Part> _parts;
|
||||||
bool _needBubble = false;
|
bool _needBubble = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -21,6 +21,12 @@ struct HistoryMessageVia;
|
||||||
struct HistoryMessageReply;
|
struct HistoryMessageReply;
|
||||||
struct HistoryMessageForwarded;
|
struct HistoryMessageForwarded;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
enum class CallFinishReason : char;
|
||||||
|
struct Invoice;
|
||||||
|
struct Call;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Media {
|
namespace Media {
|
||||||
namespace Clip {
|
namespace Clip {
|
||||||
class Playback;
|
class Playback;
|
||||||
|
@ -35,12 +41,6 @@ TextWithEntities WithCaptionSelectedText(
|
||||||
const QString &attachType,
|
const QString &attachType,
|
||||||
const Text &caption,
|
const Text &caption,
|
||||||
TextSelection selection);
|
TextSelection selection);
|
||||||
QString WithCaptionNotificationText(
|
|
||||||
const QString &attachType,
|
|
||||||
const Text &caption);
|
|
||||||
QString WithCaptionDialogsText(
|
|
||||||
const QString &attachType,
|
|
||||||
const Text &caption);
|
|
||||||
|
|
||||||
class HistoryFileMedia : public HistoryMedia {
|
class HistoryFileMedia : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
|
@ -56,7 +56,7 @@ public:
|
||||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||||
|
|
||||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
void refreshParentId(not_null<Element*> realParent) override;
|
||||||
|
|
||||||
bool allowsFastShare() const override {
|
bool allowsFastShare() const override {
|
||||||
return true;
|
return true;
|
||||||
|
@ -128,22 +128,22 @@ protected:
|
||||||
class HistoryPhoto : public HistoryFileMedia {
|
class HistoryPhoto : public HistoryFileMedia {
|
||||||
public:
|
public:
|
||||||
HistoryPhoto(
|
HistoryPhoto(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
const QString &caption);
|
const QString &caption);
|
||||||
HistoryPhoto(
|
HistoryPhoto(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<PeerData*> chat,
|
not_null<PeerData*> chat,
|
||||||
not_null<PhotoData*> photo,
|
not_null<PhotoData*> photo,
|
||||||
int width);
|
int width);
|
||||||
HistoryPhoto(
|
HistoryPhoto(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<PeerData*> chat,
|
not_null<PeerData*> chat,
|
||||||
const MTPDphoto &photo,
|
const MTPDphoto &photo,
|
||||||
int width);
|
int width);
|
||||||
HistoryPhoto(
|
HistoryPhoto(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<Element*> realParent,
|
||||||
const HistoryPhoto &other);
|
const HistoryPhoto &other);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
@ -151,8 +151,8 @@ public:
|
||||||
return MediaTypePhoto;
|
return MediaTypePhoto;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
std::unique_ptr<HistoryMedia> clone(
|
||||||
not_null<HistoryItem*> newParent,
|
not_null<Element*> newParent,
|
||||||
not_null<HistoryItem*> realParent) const override {
|
not_null<Element*> realParent) const override {
|
||||||
return std::make_unique<HistoryPhoto>(newParent, realParent, *this);
|
return std::make_unique<HistoryPhoto>(newParent, realParent, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,12 +171,8 @@ public:
|
||||||
return !_caption.isEmpty();
|
return !_caption.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
QString inDialogsText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
|
||||||
|
|
||||||
PhotoData *getPhoto() const override {
|
PhotoData *getPhoto() const override {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
@ -199,12 +195,6 @@ public:
|
||||||
QPoint point,
|
QPoint point,
|
||||||
HistoryStateRequest request) const override;
|
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 {
|
bool hasReplyPreview() const override {
|
||||||
return !_data->thumb->isNull();
|
return !_data->thumb->isNull();
|
||||||
}
|
}
|
||||||
|
@ -220,9 +210,6 @@ public:
|
||||||
bool skipBubbleTail() const override {
|
bool skipBubbleTail() const override {
|
||||||
return isBubbleBottom() && _caption.isEmpty();
|
return isBubbleBottom() && _caption.isEmpty();
|
||||||
}
|
}
|
||||||
bool canEditCaption() const override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool isReadyForOpen() const override {
|
bool isReadyForOpen() const override {
|
||||||
return _data->loaded();
|
return _data->loaded();
|
||||||
}
|
}
|
||||||
|
@ -254,20 +241,20 @@ private:
|
||||||
class HistoryVideo : public HistoryFileMedia {
|
class HistoryVideo : public HistoryFileMedia {
|
||||||
public:
|
public:
|
||||||
HistoryVideo(
|
HistoryVideo(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const QString &caption);
|
const QString &caption);
|
||||||
HistoryVideo(
|
HistoryVideo(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<Element*> realParent,
|
||||||
const HistoryVideo &other);
|
const HistoryVideo &other);
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeVideo;
|
return MediaTypeVideo;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
std::unique_ptr<HistoryMedia> clone(
|
||||||
not_null<HistoryItem*> newParent,
|
not_null<Element*> newParent,
|
||||||
not_null<HistoryItem*> realParent) const override {
|
not_null<Element*> realParent) const override {
|
||||||
return std::make_unique<HistoryVideo>(newParent, realParent, *this);
|
return std::make_unique<HistoryVideo>(newParent, realParent, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,12 +273,8 @@ public:
|
||||||
return !_caption.isEmpty();
|
return !_caption.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
QString inDialogsText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
|
||||||
|
|
||||||
DocumentData *getDocument() const override {
|
DocumentData *getDocument() const override {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
@ -318,12 +301,6 @@ public:
|
||||||
return _data->uploading();
|
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 {
|
bool hasReplyPreview() const override {
|
||||||
return !_data->thumb->isNull();
|
return !_data->thumb->isNull();
|
||||||
}
|
}
|
||||||
|
@ -339,9 +316,6 @@ public:
|
||||||
bool skipBubbleTail() const override {
|
bool skipBubbleTail() const override {
|
||||||
return isBubbleBottom() && _caption.isEmpty();
|
return isBubbleBottom() && _caption.isEmpty();
|
||||||
}
|
}
|
||||||
bool canEditCaption() const override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float64 dataProgress() const override;
|
float64 dataProgress() const override;
|
||||||
|
@ -366,15 +340,14 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class HistoryDocument : public HistoryFileMedia, public RuntimeComposer {
|
class HistoryDocument
|
||||||
|
: public HistoryFileMedia
|
||||||
|
, public RuntimeComposer<HistoryDocument> {
|
||||||
public:
|
public:
|
||||||
HistoryDocument(
|
HistoryDocument(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const QString &caption);
|
const QString &caption);
|
||||||
HistoryDocument(
|
|
||||||
not_null<HistoryItem*> parent,
|
|
||||||
const HistoryDocument &other);
|
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return _data->isVoiceMessage()
|
return _data->isVoiceMessage()
|
||||||
|
@ -383,13 +356,6 @@ public:
|
||||||
? MediaTypeMusicFile
|
? MediaTypeMusicFile
|
||||||
: MediaTypeFile);
|
: MediaTypeFile);
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistoryDocument>(newParent, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -401,12 +367,8 @@ public:
|
||||||
uint16 fullSelectionLength() const override;
|
uint16 fullSelectionLength() const override;
|
||||||
bool hasTextForCopy() const override;
|
bool hasTextForCopy() const override;
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
QString inDialogsText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
|
||||||
|
|
||||||
bool uploading() const override {
|
bool uploading() const override {
|
||||||
return _data->uploading();
|
return _data->uploading();
|
||||||
}
|
}
|
||||||
|
@ -417,12 +379,6 @@ public:
|
||||||
|
|
||||||
bool playInline(bool autoplay) override;
|
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 {
|
bool hasReplyPreview() const override {
|
||||||
return !_data->thumb->isNull();
|
return !_data->thumb->isNull();
|
||||||
}
|
}
|
||||||
|
@ -439,15 +395,12 @@ public:
|
||||||
bool hideForwardedFrom() const override {
|
bool hideForwardedFrom() const override {
|
||||||
return _data->isSong();
|
return _data->isSong();
|
||||||
}
|
}
|
||||||
bool canEditCaption() const override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void step_voiceProgress(float64 ms, bool timer);
|
void step_voiceProgress(float64 ms, bool timer);
|
||||||
|
|
||||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||||
|
|
||||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
void refreshParentId(not_null<Element*> realParent) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float64 dataProgress() const override;
|
float64 dataProgress() const override;
|
||||||
|
@ -476,21 +429,13 @@ private:
|
||||||
class HistoryGif : public HistoryFileMedia {
|
class HistoryGif : public HistoryFileMedia {
|
||||||
public:
|
public:
|
||||||
HistoryGif(
|
HistoryGif(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const QString &caption);
|
const QString &caption);
|
||||||
HistoryGif(not_null<HistoryItem*> parent, const HistoryGif &other);
|
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeGif;
|
return MediaTypeGif;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistoryGif>(newParent, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -507,12 +452,8 @@ public:
|
||||||
return !_caption.isEmpty();
|
return !_caption.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
QString inDialogsText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
|
||||||
|
|
||||||
bool uploading() const override {
|
bool uploading() const override {
|
||||||
return _data->uploading();
|
return _data->uploading();
|
||||||
}
|
}
|
||||||
|
@ -528,12 +469,6 @@ public:
|
||||||
void stopInline() override;
|
void stopInline() override;
|
||||||
bool isRoundVideoPlaying() const 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 {
|
bool hasReplyPreview() const override {
|
||||||
return !_data->thumb->isNull();
|
return !_data->thumb->isNull();
|
||||||
}
|
}
|
||||||
|
@ -551,9 +486,6 @@ public:
|
||||||
bool skipBubbleTail() const override {
|
bool skipBubbleTail() const override {
|
||||||
return isBubbleBottom() && _caption.isEmpty();
|
return isBubbleBottom() && _caption.isEmpty();
|
||||||
}
|
}
|
||||||
bool canEditCaption() const override {
|
|
||||||
return !_data->isVideoMessage();
|
|
||||||
}
|
|
||||||
bool isReadyForOpen() const override {
|
bool isReadyForOpen() const override {
|
||||||
return _data->loaded();
|
return _data->loaded();
|
||||||
}
|
}
|
||||||
|
@ -600,19 +532,12 @@ private:
|
||||||
class HistorySticker : public HistoryMedia {
|
class HistorySticker : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistorySticker(
|
HistorySticker(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<DocumentData*> document);
|
not_null<DocumentData*> document);
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeSticker;
|
return MediaTypeSticker;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistorySticker>(newParent, _data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -627,19 +552,12 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
DocumentData *getDocument() const override {
|
DocumentData *getDocument() const override {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void attachToParent() override;
|
|
||||||
void detachFromParent() override;
|
|
||||||
|
|
||||||
void updateSentMedia(const MTPMessageMedia &media) override;
|
|
||||||
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
|
|
||||||
|
|
||||||
bool hasReplyPreview() const override {
|
bool hasReplyPreview() const override {
|
||||||
return !_data->thumb->isNull();
|
return !_data->thumb->isNull();
|
||||||
}
|
}
|
||||||
|
@ -674,8 +592,8 @@ private:
|
||||||
class HistoryContact : public HistoryMedia {
|
class HistoryContact : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistoryContact(
|
HistoryContact(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
int32 userId,
|
UserId userId,
|
||||||
const QString &first,
|
const QString &first,
|
||||||
const QString &last,
|
const QString &last,
|
||||||
const QString &phone);
|
const QString &phone);
|
||||||
|
@ -683,18 +601,6 @@ public:
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeContact;
|
return MediaTypeContact;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistoryContact>(
|
|
||||||
newParent,
|
|
||||||
_userId,
|
|
||||||
_fname,
|
|
||||||
_lname,
|
|
||||||
_phone);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -706,14 +612,11 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
void attachToParent() override;
|
void attachToParent() override;
|
||||||
void detachFromParent() override;
|
void detachFromParent() override;
|
||||||
|
|
||||||
void updateSentMedia(const MTPMessageMedia &media) override;
|
|
||||||
|
|
||||||
bool needsBubble() const override {
|
bool needsBubble() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -736,7 +639,7 @@ public:
|
||||||
private:
|
private:
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
|
|
||||||
int32 _userId = 0;
|
UserId _userId = 0;
|
||||||
UserData *_contact = nullptr;
|
UserData *_contact = nullptr;
|
||||||
|
|
||||||
int _phonew = 0;
|
int _phonew = 0;
|
||||||
|
@ -753,17 +656,12 @@ private:
|
||||||
class HistoryCall : public HistoryMedia {
|
class HistoryCall : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistoryCall(
|
HistoryCall(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
const MTPDmessageActionPhoneCall &call);
|
not_null<Data::Call*> call);
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeCall;
|
return MediaTypeCall;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Unexpected("Clone HistoryCall.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -775,7 +673,6 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
bool needsBubble() const override {
|
bool needsBubble() const override {
|
||||||
|
@ -785,22 +682,14 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class FinishReason {
|
Data::CallFinishReason reason() const;
|
||||||
Missed,
|
|
||||||
Busy,
|
|
||||||
Disconnected,
|
|
||||||
Hangup,
|
|
||||||
};
|
|
||||||
FinishReason reason() const {
|
|
||||||
return _reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using FinishReason = Data::CallFinishReason;
|
||||||
|
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
|
|
||||||
static FinishReason GetReason(const MTPDmessageActionPhoneCall &call);
|
FinishReason _reason;
|
||||||
|
|
||||||
FinishReason _reason = FinishReason::Missed;
|
|
||||||
int _duration = 0;
|
int _duration = 0;
|
||||||
|
|
||||||
QString _text;
|
QString _text;
|
||||||
|
@ -813,24 +702,14 @@ private:
|
||||||
class HistoryWebPage : public HistoryMedia {
|
class HistoryWebPage : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistoryWebPage(
|
HistoryWebPage(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<WebPageData*> data);
|
not_null<WebPageData*> data);
|
||||||
HistoryWebPage(
|
|
||||||
not_null<HistoryItem*> parent,
|
|
||||||
const HistoryWebPage &other);
|
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeWebPage;
|
return MediaTypeWebPage;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistoryWebPage>(newParent, *this);
|
void refreshParentId(not_null<Element*> realParent) override;
|
||||||
}
|
|
||||||
|
|
||||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -930,21 +809,16 @@ private:
|
||||||
|
|
||||||
class HistoryGame : public HistoryMedia {
|
class HistoryGame : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistoryGame(not_null<HistoryItem*> parent, not_null<GameData*> data);
|
HistoryGame(
|
||||||
HistoryGame(not_null<HistoryItem*> parent, const HistoryGame &other);
|
not_null<Element*> parent,
|
||||||
|
not_null<GameData*> data,
|
||||||
|
const TextWithEntities &consumed);
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeGame;
|
return MediaTypeGame;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistoryGame>(newParent, *this);
|
void refreshParentId(not_null<Element*> realParent) override;
|
||||||
}
|
|
||||||
|
|
||||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -961,7 +835,6 @@ public:
|
||||||
bool hasTextForCopy() const override {
|
bool hasTextForCopy() const override {
|
||||||
return false; // we do not add _title and _description in FullSelection text copy.
|
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 {
|
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
|
||||||
return _attach && _attach->toggleSelectionByHandlerClick(p);
|
return _attach && _attach->toggleSelectionByHandlerClick(p);
|
||||||
|
@ -970,7 +843,6 @@ public:
|
||||||
return _attach && _attach->dragItemByHandler(p);
|
return _attach && _attach->dragItemByHandler(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||||
|
@ -1004,9 +876,6 @@ public:
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateSentMedia(const MTPMessageMedia &media) override;
|
|
||||||
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
|
|
||||||
|
|
||||||
bool needsBubble() const override {
|
bool needsBubble() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1045,24 +914,14 @@ private:
|
||||||
class HistoryInvoice : public HistoryMedia {
|
class HistoryInvoice : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistoryInvoice(
|
HistoryInvoice(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
const MTPDmessageMediaInvoice &data);
|
not_null<Data::Invoice*> invoice);
|
||||||
HistoryInvoice(
|
|
||||||
not_null<HistoryItem*> parent,
|
|
||||||
const HistoryInvoice &other);
|
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeInvoice;
|
return MediaTypeInvoice;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistoryInvoice>(newParent, *this);
|
void refreshParentId(not_null<Element*> realParent) override;
|
||||||
}
|
|
||||||
|
|
||||||
void refreshParentId(not_null<HistoryItem*> realParent) override;
|
|
||||||
|
|
||||||
MsgId getReceiptMsgId() const {
|
MsgId getReceiptMsgId() const {
|
||||||
return _receiptMsgId;
|
return _receiptMsgId;
|
||||||
|
@ -1092,7 +951,6 @@ public:
|
||||||
return _attach && _attach->dragItemByHandler(p);
|
return _attach && _attach->dragItemByHandler(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||||
|
@ -1123,7 +981,7 @@ private:
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
void fillFromData(const MTPDmessageMediaInvoice &data);
|
void fillFromData(not_null<Data::Invoice*> invoice);
|
||||||
|
|
||||||
TextSelection toDescriptionSelection(TextSelection selection) const;
|
TextSelection toDescriptionSelection(TextSelection selection) const;
|
||||||
TextSelection fromDescriptionSelection(TextSelection selection) const;
|
TextSelection fromDescriptionSelection(TextSelection selection) const;
|
||||||
|
@ -1148,24 +1006,14 @@ struct LocationData;
|
||||||
class HistoryLocation : public HistoryMedia {
|
class HistoryLocation : public HistoryMedia {
|
||||||
public:
|
public:
|
||||||
HistoryLocation(
|
HistoryLocation(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<Element*> parent,
|
||||||
const LocationCoords &coords,
|
not_null<LocationData*> location,
|
||||||
const QString &title = QString(),
|
const QString &title = QString(),
|
||||||
const QString &description = QString());
|
const QString &description = QString());
|
||||||
HistoryLocation(
|
|
||||||
not_null<HistoryItem*> parent,
|
|
||||||
const HistoryLocation &other);
|
|
||||||
|
|
||||||
HistoryMediaType type() const override {
|
HistoryMediaType type() const override {
|
||||||
return MediaTypeLocation;
|
return MediaTypeLocation;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HistoryMedia> clone(
|
|
||||||
not_null<HistoryItem*> newParent,
|
|
||||||
not_null<HistoryItem*> realParent) const override {
|
|
||||||
Expects(newParent == realParent);
|
|
||||||
|
|
||||||
return std::make_unique<HistoryLocation>(newParent, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override;
|
||||||
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
HistoryTextState getState(QPoint point, HistoryStateRequest request) const override;
|
||||||
|
@ -1187,8 +1035,6 @@ public:
|
||||||
return p == _link;
|
return p == _link;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString notificationText() const override;
|
|
||||||
QString inDialogsText() const override;
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
|
||||||
bool needsBubble() const override;
|
bool needsBubble() const override;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -22,6 +22,7 @@ QString GetErrorTextForForward(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const HistoryItemsList &items);
|
const HistoryItemsList &items);
|
||||||
void FastShareMessage(not_null<HistoryItem*> item);
|
void FastShareMessage(not_null<HistoryItem*> item);
|
||||||
|
QString FormatViewsCount(int views);
|
||||||
|
|
||||||
class HistoryMessage
|
class HistoryMessage
|
||||||
: public HistoryItem
|
: public HistoryItem
|
||||||
|
@ -142,37 +143,22 @@ public:
|
||||||
markup);
|
markup);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initTime();
|
|
||||||
void initMedia(const MTPMessageMedia *media);
|
void initMedia(const MTPMessageMedia *media);
|
||||||
void initMediaFromDocument(DocumentData *doc, const QString &caption);
|
|
||||||
|
|
||||||
int32 plainMaxWidth() const;
|
int32 plainMaxWidth() const;
|
||||||
|
|
||||||
bool drawBubble() const;
|
bool allowsForward() const override;
|
||||||
bool hasBubble() const override {
|
bool allowsEdit(const QDateTime &now) const override;
|
||||||
return drawBubble();
|
|
||||||
}
|
|
||||||
bool hasFastReply() const;
|
|
||||||
bool displayFastReply() const;
|
|
||||||
bool displayForwardedFrom() const;
|
|
||||||
bool uploading() const;
|
bool uploading() const;
|
||||||
bool displayRightAction() const override;
|
|
||||||
|
|
||||||
void applyGroupAdminChanges(
|
void applyGroupAdminChanges(
|
||||||
const base::flat_map<UserId, bool> &changes) override;
|
const base::flat_map<UserId, bool> &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 setViewsCount(int32 count) override;
|
||||||
void setRealId(MsgId newId) override;
|
void setRealId(MsgId newId) override;
|
||||||
ClickHandlerPtr rightActionLink() const override;
|
|
||||||
|
|
||||||
void dependencyItemRemoved(HistoryItem *dependency) 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;
|
QString notificationHeader() const override;
|
||||||
|
|
||||||
void applyEdition(const MTPDmessage &message) override;
|
void applyEdition(const MTPDmessage &message) override;
|
||||||
|
@ -186,20 +172,10 @@ public:
|
||||||
void eraseFromUnreadMentions() override;
|
void eraseFromUnreadMentions() override;
|
||||||
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||||
|
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
|
||||||
void setText(const TextWithEntities &textWithEntities) override;
|
void setText(const TextWithEntities &textWithEntities) override;
|
||||||
TextWithEntities originalText() const override;
|
TextWithEntities originalText() const override;
|
||||||
bool textHasLinks() 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;
|
int viewsCount() const override;
|
||||||
not_null<PeerData*> displayFrom() const;
|
not_null<PeerData*> displayFrom() const;
|
||||||
bool updateDependencyItem() override;
|
bool updateDependencyItem() override;
|
||||||
|
@ -220,9 +196,6 @@ public:
|
||||||
|
|
||||||
~HistoryMessage();
|
~HistoryMessage();
|
||||||
|
|
||||||
protected:
|
|
||||||
void refreshEditedBadge() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HistoryMessage(
|
HistoryMessage(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
|
@ -295,16 +268,9 @@ private:
|
||||||
void replaceBuyWithReceiptInMarkup();
|
void replaceBuyWithReceiptInMarkup();
|
||||||
|
|
||||||
void applyEditionToEmpty();
|
void applyEditionToEmpty();
|
||||||
QDateTime displayedEditDate(bool hasViaBotOrInlineMarkup) const;
|
|
||||||
const HistoryMessageEdited *displayedEditBadge() const;
|
|
||||||
HistoryMessageEdited *displayedEditBadge();
|
|
||||||
|
|
||||||
void setMedia(const MTPMessageMedia *media);
|
|
||||||
void setReplyMarkup(const MTPReplyMarkup *markup);
|
void setReplyMarkup(const MTPReplyMarkup *markup);
|
||||||
|
|
||||||
bool displayFastShare() const;
|
|
||||||
bool displayGoToOriginal() const;
|
|
||||||
|
|
||||||
struct CreateConfig;
|
struct CreateConfig;
|
||||||
void createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, UserId viaBotId, const QString &postAuthor, const MTPReplyMarkup &markup);
|
void createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, UserId viaBotId, const QString &postAuthor, const MTPReplyMarkup &markup);
|
||||||
void createComponents(const CreateConfig &config);
|
void createComponents(const CreateConfig &config);
|
||||||
|
@ -315,8 +281,6 @@ private:
|
||||||
QString _timeText;
|
QString _timeText;
|
||||||
int _timeWidth = 0;
|
int _timeWidth = 0;
|
||||||
|
|
||||||
mutable ClickHandlerPtr _rightActionLink;
|
|
||||||
mutable ClickHandlerPtr _fastReplyLink;
|
|
||||||
mutable int32 _fromNameVersion = 0;
|
mutable int32 _fromNameVersion = 0;
|
||||||
|
|
||||||
friend class HistoryView::Element;
|
friend class HistoryView::Element;
|
||||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_service_message.h"
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
|
@ -204,7 +205,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
case mtpc_messageActionChatEditPhoto: {
|
case mtpc_messageActionChatEditPhoto: {
|
||||||
auto &photo = action.c_messageActionChatEditPhoto().vphoto;
|
auto &photo = action.c_messageActionChatEditPhoto().vphoto;
|
||||||
if (photo.type() == mtpc_photo) {
|
if (photo.type() == mtpc_photo) {
|
||||||
_media = std::make_unique<HistoryPhoto>(this, history()->peer, photo.c_photo(), st::msgServicePhotoWidth);
|
_media = std::make_unique<Data::MediaPhoto>(
|
||||||
|
this,
|
||||||
|
history()->peer,
|
||||||
|
App::feedPhoto(photo.c_photo()));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -268,42 +272,12 @@ HistoryService::PreparedText HistoryService::preparePinnedText() {
|
||||||
auto result = PreparedText {};
|
auto result = PreparedText {};
|
||||||
auto pinned = Get<HistoryServicePinned>();
|
auto pinned = Get<HistoryServicePinned>();
|
||||||
if (pinned && pinned->msg) {
|
if (pinned && pinned->msg) {
|
||||||
auto mediaText = ([pinned]() -> QString {
|
const auto mediaText = [&] {
|
||||||
auto media = pinned->msg->getMedia();
|
if (const auto media = pinned->msg->media()) {
|
||||||
switch (media ? media->type() : MediaTypeCount) {
|
return media->pinnedTextSubstring();
|
||||||
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<HistorySticker*>(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<HistoryGame*>(media)->game()->title;
|
|
||||||
return lng_action_pinned_media_game(lt_game, title);
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
return QString();
|
return QString();
|
||||||
})();
|
}();
|
||||||
|
|
||||||
result.links.push_back(fromLink());
|
result.links.push_back(fromLink());
|
||||||
result.links.push_back(pinned->lnk);
|
result.links.push_back(pinned->lnk);
|
||||||
if (mediaText.isEmpty()) {
|
if (mediaText.isEmpty()) {
|
||||||
|
@ -344,8 +318,8 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() {
|
||||||
|
|
||||||
auto computeGameTitle = [gamescore, &result]() -> QString {
|
auto computeGameTitle = [gamescore, &result]() -> QString {
|
||||||
if (gamescore && gamescore->msg) {
|
if (gamescore && gamescore->msg) {
|
||||||
if (const auto media = gamescore->msg->getMedia()) {
|
if (const auto media = gamescore->msg->media()) {
|
||||||
if (media->type() == MediaTypeGame) {
|
if (const auto game = media->game()) {
|
||||||
const auto row = 0;
|
const auto row = 0;
|
||||||
const auto column = 0;
|
const auto column = 0;
|
||||||
result.links.push_back(
|
result.links.push_back(
|
||||||
|
@ -353,7 +327,7 @@ HistoryService::PreparedText HistoryService::prepareGameScoreText() {
|
||||||
row,
|
row,
|
||||||
column,
|
column,
|
||||||
gamescore->msg->fullId()));
|
gamescore->msg->fullId()));
|
||||||
auto titleText = static_cast<HistoryGame*>(media)->game()->title;
|
auto titleText = game->title;
|
||||||
return textcmdLink(result.links.size(), titleText);
|
return textcmdLink(result.links.size(), titleText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,11 +378,11 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
|
||||||
auto result = PreparedText {};
|
auto result = PreparedText {};
|
||||||
auto payment = Get<HistoryServicePayment>();
|
auto payment = Get<HistoryServicePayment>();
|
||||||
|
|
||||||
auto invoiceTitle = ([payment]() -> QString {
|
auto invoiceTitle = [&] {
|
||||||
if (payment && payment->msg) {
|
if (payment && payment->msg) {
|
||||||
if (auto media = payment->msg->getMedia()) {
|
if (const auto media = payment->msg->media()) {
|
||||||
if (media->type() == MediaTypeInvoice) {
|
if (const auto invoice = media->invoice()) {
|
||||||
return static_cast<HistoryInvoice*>(media)->getTitle();
|
return invoice->title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lang(lng_deleted_message);
|
return lang(lng_deleted_message);
|
||||||
|
@ -416,7 +390,7 @@ HistoryService::PreparedText HistoryService::preparePaymentSentText() {
|
||||||
return lang(lng_contacts_loading);
|
return lang(lng_contacts_loading);
|
||||||
}
|
}
|
||||||
return QString();
|
return QString();
|
||||||
})();
|
}();
|
||||||
|
|
||||||
if (invoiceTitle.isEmpty()) {
|
if (invoiceTitle.isEmpty()) {
|
||||||
result.text = lng_action_payment_done(lt_amount, payment->amount, lt_user, history()->peer->name);
|
result.text = lng_action_payment_done(lt_amount, payment->amount, lt_user, history()->peer->name);
|
||||||
|
@ -431,16 +405,33 @@ HistoryService::HistoryService(not_null<History*> history, const MTPDmessage &me
|
||||||
createFromMtp(message);
|
createFromMtp(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryService::HistoryService(not_null<History*> history, const MTPDmessageService &message) :
|
HistoryService::HistoryService(
|
||||||
HistoryItem(history, message.vid.v, mtpCastFlags(message.vflags.v), ::date(message.vdate), message.has_from_id() ? message.vfrom_id.v : 0) {
|
not_null<History*> 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);
|
createFromMtp(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryService::HistoryService(not_null<History*> history, MsgId msgId, QDateTime date, const PreparedText &message, MTPDmessage::Flags flags, int32 from, PhotoData *photo) :
|
HistoryService::HistoryService(
|
||||||
HistoryItem(history, msgId, flags, date, from) {
|
not_null<History*> history,
|
||||||
|
MsgId msgId,
|
||||||
|
QDateTime date,
|
||||||
|
const PreparedText &message,
|
||||||
|
MTPDmessage::Flags flags,
|
||||||
|
UserId from,
|
||||||
|
PhotoData *photo)
|
||||||
|
: HistoryItem(history, msgId, flags, date, from) {
|
||||||
setServiceText(message);
|
setServiceText(message);
|
||||||
if (photo) {
|
if (photo) {
|
||||||
_media = std::make_unique<HistoryPhoto>(this, history->peer, photo, st::msgServicePhotoWidth);
|
_media = std::make_unique<Data::MediaPhoto>(
|
||||||
|
this,
|
||||||
|
history->peer,
|
||||||
|
photo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,10 +442,6 @@ bool HistoryService::updateDependencyItem() {
|
||||||
return HistoryItem::updateDependencyItem();
|
return HistoryItem::updateDependencyItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryService::selectedText(TextSelection selection) const {
|
|
||||||
return _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString HistoryService::inDialogsText(DrawInDialog way) const {
|
QString HistoryService::inDialogsText(DrawInDialog way) const {
|
||||||
return textcmdLink(1, TextUtilities::Clean(notificationText()));
|
return textcmdLink(1, TextUtilities::Clean(notificationText()));
|
||||||
}
|
}
|
||||||
|
@ -599,16 +586,14 @@ void HistoryService::applyEdition(const MTPDmessageService &message) {
|
||||||
void HistoryService::removeMedia() {
|
void HistoryService::removeMedia() {
|
||||||
if (!_media) return;
|
if (!_media) return;
|
||||||
|
|
||||||
bool mediaWasDisplayed = _media->isDisplayed();
|
|
||||||
_media.reset();
|
_media.reset();
|
||||||
if (mediaWasDisplayed) {
|
|
||||||
_textWidth = -1;
|
_textWidth = -1;
|
||||||
_textHeight = 0;
|
_textHeight = 0;
|
||||||
}
|
Auth().data().requestItemViewResize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const {
|
Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const {
|
||||||
if (auto media = getMedia()) {
|
if (auto media = this->media()) {
|
||||||
return media->sharedMediaTypes();
|
return media->sharedMediaTypes();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -20,24 +20,24 @@ struct HistoryServiceDependentData {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryServicePinned
|
struct HistoryServicePinned
|
||||||
: public RuntimeComponent<HistoryServicePinned>
|
: public RuntimeComponent<HistoryServicePinned, HistoryItem>
|
||||||
, public HistoryServiceDependentData {
|
, public HistoryServiceDependentData {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryServiceGameScore
|
struct HistoryServiceGameScore
|
||||||
: public RuntimeComponent<HistoryServiceGameScore>
|
: public RuntimeComponent<HistoryServiceGameScore, HistoryItem>
|
||||||
, public HistoryServiceDependentData {
|
, public HistoryServiceDependentData {
|
||||||
int score = 0;
|
int score = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryServicePayment
|
struct HistoryServicePayment
|
||||||
: public RuntimeComponent<HistoryServicePayment>
|
: public RuntimeComponent<HistoryServicePayment, HistoryItem>
|
||||||
, public HistoryServiceDependentData {
|
, public HistoryServiceDependentData {
|
||||||
QString amount;
|
QString amount;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryServiceSelfDestruct
|
struct HistoryServiceSelfDestruct
|
||||||
: public RuntimeComponent<HistoryServiceSelfDestruct> {
|
: public RuntimeComponent<HistoryServiceSelfDestruct, HistoryItem> {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Photo,
|
Photo,
|
||||||
Video,
|
Video,
|
||||||
|
@ -82,12 +82,6 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] TextSelection adjustSelection(
|
|
||||||
TextSelection selection,
|
|
||||||
TextSelectType type) const override {
|
|
||||||
return _text.adjustSelection(selection, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyEdition(const MTPDmessageService &message) override;
|
void applyEdition(const MTPDmessageService &message) override;
|
||||||
TimeMs getSelfDestructIn(TimeMs now) override;
|
TimeMs getSelfDestructIn(TimeMs now) override;
|
||||||
|
|
||||||
|
@ -99,7 +93,6 @@ public:
|
||||||
bool serviceMsg() const override {
|
bool serviceMsg() const override {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
TextWithEntities selectedText(TextSelection selection) const override;
|
|
||||||
QString inDialogsText(DrawInDialog way) const override;
|
QString inDialogsText(DrawInDialog way) const override;
|
||||||
QString inReplyText() const override;
|
QString inReplyText() const override;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "inline_bots/inline_bot_result.h"
|
#include "inline_bots/inline_bot_result.h"
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
|
@ -648,10 +649,12 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
|
||||||
Auth().data().itemLayoutChanged(
|
Auth().data().itemLayoutChanged(
|
||||||
) | rpl::start_with_next([this](auto item) {
|
) | rpl::start_with_next([this](auto item) {
|
||||||
if (_peer && _list) {
|
if (_peer && _list) {
|
||||||
if (item->isUnderCursor()) {
|
if (const auto view = item->mainView()) {
|
||||||
|
if (view->isUnderCursor()) {
|
||||||
_list->onUpdateSelected();
|
_list->onUpdateSelected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
_topBar->membersShowAreaActive(
|
_topBar->membersShowAreaActive(
|
||||||
) | rpl::start_with_next([this](bool active) {
|
) | rpl::start_with_next([this](bool active) {
|
||||||
|
@ -714,7 +717,10 @@ void HistoryWidget::animatedScrollToItem(MsgId msgId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto scrollTo = snap(itemTopForHighlight(to), 0, _scroll->scrollTopMax());
|
auto scrollTo = snap(
|
||||||
|
itemTopForHighlight(to->mainView()),
|
||||||
|
0,
|
||||||
|
_scroll->scrollTopMax());
|
||||||
animatedScrollToY(scrollTo, to);
|
animatedScrollToY(scrollTo, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,9 +775,10 @@ void HistoryWidget::scrollToAnimationCallback(FullMsgId attachToId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::enqueueMessageHighlight(not_null<HistoryItem*> item) {
|
void HistoryWidget::enqueueMessageHighlight(
|
||||||
if (const auto group = item->getFullGroup()) {
|
not_null<HistoryView::Element*> view) {
|
||||||
item = group->leader;
|
if (const auto group = view->getFullGroup()) {
|
||||||
|
view = group->leader;
|
||||||
}
|
}
|
||||||
auto enqueueMessageId = [this](MsgId universalId) {
|
auto enqueueMessageId = [this](MsgId universalId) {
|
||||||
if (_highlightQueue.empty() && !_highlightTimer.isActive()) {
|
if (_highlightQueue.empty() && !_highlightTimer.isActive()) {
|
||||||
|
@ -782,6 +789,7 @@ void HistoryWidget::enqueueMessageHighlight(not_null<HistoryItem*> item) {
|
||||||
checkNextHighlight();
|
checkNextHighlight();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const auto item = view->data();
|
||||||
if (item->history() == _history) {
|
if (item->history() == _history) {
|
||||||
enqueueMessageId(item->id);
|
enqueueMessageId(item->id);
|
||||||
} else if (item->history() == _migrated) {
|
} else if (item->history() == _migrated) {
|
||||||
|
@ -874,12 +882,10 @@ void HistoryWidget::clearHighlightMessages() {
|
||||||
stopMessageHighlight();
|
stopMessageHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
int HistoryWidget::itemTopForHighlight(not_null<HistoryItem*> item) const {
|
int HistoryWidget::itemTopForHighlight(
|
||||||
const auto view = item->mainView();
|
not_null<HistoryView::Element*> view) const {
|
||||||
Assert(view != nullptr);
|
if (const auto group = view->getFullGroup()) {
|
||||||
|
view = group->leader;
|
||||||
if (const auto group = item->getFullGroup()) {
|
|
||||||
item = group->leader;
|
|
||||||
}
|
}
|
||||||
auto itemTop = _list->itemTop(view);
|
auto itemTop = _list->itemTop(view);
|
||||||
Assert(itemTop >= 0);
|
Assert(itemTop >= 0);
|
||||||
|
@ -4354,8 +4360,8 @@ void HistoryWidget::onThumbDocumentUploaded(
|
||||||
|
|
||||||
void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
|
void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
|
||||||
if (const auto item = App::histItemById(newId)) {
|
if (const auto item = App::histItemById(newId)) {
|
||||||
const auto photo = item->getMedia()
|
const auto photo = item->media()
|
||||||
? item->getMedia()->getPhoto()
|
? item->media()->photo()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0);
|
updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0);
|
||||||
Auth().data().requestItemRepaint(item);
|
Auth().data().requestItemRepaint(item);
|
||||||
|
@ -4364,8 +4370,8 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) {
|
||||||
|
|
||||||
void HistoryWidget::onDocumentProgress(const FullMsgId &newId) {
|
void HistoryWidget::onDocumentProgress(const FullMsgId &newId) {
|
||||||
if (const auto item = App::histItemById(newId)) {
|
if (const auto item = App::histItemById(newId)) {
|
||||||
const auto media = item->getMedia();
|
const auto media = item->media();
|
||||||
const auto document = media ? media->getDocument() : nullptr;
|
const auto document = media ? media->document() : nullptr;
|
||||||
const auto sendAction = (document && document->isVoiceMessage())
|
const auto sendAction = (document && document->isVoiceMessage())
|
||||||
? SendAction::Type::UploadVoice
|
? SendAction::Type::UploadVoice
|
||||||
: SendAction::Type::UploadFile;
|
: SendAction::Type::UploadFile;
|
||||||
|
@ -4389,8 +4395,8 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) {
|
||||||
|
|
||||||
void HistoryWidget::onDocumentFailed(const FullMsgId &newId) {
|
void HistoryWidget::onDocumentFailed(const FullMsgId &newId) {
|
||||||
if (const auto item = App::histItemById(newId)) {
|
if (const auto item = App::histItemById(newId)) {
|
||||||
const auto media = item->getMedia();
|
const auto media = item->media();
|
||||||
const auto document = media ? media->getDocument() : nullptr;
|
const auto document = media ? media->document() : nullptr;
|
||||||
const auto sendAction = (document && document->isVoiceMessage())
|
const auto sendAction = (document && document->isVoiceMessage())
|
||||||
? SendAction::Type::UploadVoice
|
? SendAction::Type::UploadVoice
|
||||||
: SendAction::Type::UploadFile;
|
: SendAction::Type::UploadFile;
|
||||||
|
@ -4666,8 +4672,11 @@ int HistoryWidget::countInitialScrollTop() {
|
||||||
setMsgId(0);
|
setMsgId(0);
|
||||||
return countInitialScrollTop();
|
return countInitialScrollTop();
|
||||||
} else {
|
} else {
|
||||||
result = itemTopForHighlight(item);
|
const auto view = item->mainView();
|
||||||
enqueueMessageHighlight(item);
|
Assert(view != nullptr);
|
||||||
|
|
||||||
|
result = itemTopForHighlight(view);
|
||||||
|
enqueueMessageHighlight(view);
|
||||||
}
|
}
|
||||||
} else if (_history->unreadBar || (_migrated && _migrated->unreadBar)) {
|
} else if (_history->unreadBar || (_migrated && _migrated->unreadBar)) {
|
||||||
result = unreadBarTop();
|
result = unreadBarTop();
|
||||||
|
@ -5076,7 +5085,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
|
||||||
}
|
}
|
||||||
} else if (e->key() == Qt::Key_Up) {
|
} else if (e->key() == Qt::Key_Up) {
|
||||||
if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) {
|
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) {
|
if (_field->isEmpty() && !_editMsgId && !_replyToId && _history->lastSentMsg) {
|
||||||
editMessage(_history->lastSentMsg);
|
editMessage(_history->lastSentMsg);
|
||||||
return;
|
return;
|
||||||
|
@ -5598,8 +5607,8 @@ void HistoryWidget::editMessage(FullMsgId itemId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (media->canEditCaption()) {
|
if (media->allowsEditCaption()) {
|
||||||
Ui::show(Box<EditCaptionBox>(media, item->fullId()));
|
Ui::show(Box<EditCaptionBox>(media, item->fullId()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5635,9 +5644,9 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
|
||||||
applyDraft(false);
|
applyDraft(false);
|
||||||
|
|
||||||
_previewData = nullptr;
|
_previewData = nullptr;
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (media->type() == MediaTypeWebPage) {
|
if (const auto page = media->webpage()) {
|
||||||
_previewData = static_cast<HistoryWebPage*>(media)->webpage();
|
_previewData = page;
|
||||||
updatePreview();
|
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());
|
(_editMsgId ? st::historyEditIcon : st::historyReplyIcon).paint(p, st::historyReplyIconPosition + QPoint(0, backy), width());
|
||||||
if (!drawWebPagePreview) {
|
if (!drawWebPagePreview) {
|
||||||
if (drawMsgText) {
|
if (drawMsgText) {
|
||||||
if (drawMsgText->getMedia() && drawMsgText->getMedia()->hasReplyPreview()) {
|
if (drawMsgText->media() && drawMsgText->media()->hasReplyPreview()) {
|
||||||
auto replyPreview = drawMsgText->getMedia()->replyPreview();
|
auto replyPreview = drawMsgText->media()->replyPreview();
|
||||||
if (!replyPreview->isNull()) {
|
if (!replyPreview->isNull()) {
|
||||||
auto to = QRect(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
|
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));
|
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());
|
st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width());
|
||||||
if (!drawWebPagePreview) {
|
if (!drawWebPagePreview) {
|
||||||
const auto firstItem = _toForward.front();
|
const auto firstItem = _toForward.front();
|
||||||
const auto firstMedia = firstItem->getMedia();
|
const auto firstMedia = firstItem->media();
|
||||||
const auto serviceColor = (_toForward.size() > 1)
|
const auto serviceColor = (_toForward.size() > 1)
|
||||||
|| (firstMedia != nullptr)
|
|| (firstMedia != nullptr)
|
||||||
|| firstItem->serviceMsg();
|
|| firstItem->serviceMsg();
|
||||||
|
@ -6498,8 +6507,8 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
|
||||||
|
|
||||||
int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip;
|
int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip;
|
||||||
if (_pinnedBar->msg) {
|
if (_pinnedBar->msg) {
|
||||||
if (_pinnedBar->msg->getMedia() && _pinnedBar->msg->getMedia()->hasReplyPreview()) {
|
if (_pinnedBar->msg->media() && _pinnedBar->msg->media()->hasReplyPreview()) {
|
||||||
ImagePtr replyPreview = _pinnedBar->msg->getMedia()->replyPreview();
|
ImagePtr replyPreview = _pinnedBar->msg->media()->replyPreview();
|
||||||
if (!replyPreview->isNull()) {
|
if (!replyPreview->isNull()) {
|
||||||
QRect to(left, top, st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
|
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));
|
p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height(), ImageRoundRadius::Small));
|
||||||
|
|
|
@ -246,7 +246,7 @@ public:
|
||||||
|
|
||||||
bool touchScroll(const QPoint &delta);
|
bool touchScroll(const QPoint &delta);
|
||||||
|
|
||||||
void enqueueMessageHighlight(not_null<HistoryItem*> item);
|
void enqueueMessageHighlight(not_null<HistoryView::Element*> view);
|
||||||
TimeMs highlightStartTime(not_null<const HistoryItem*> item) const;
|
TimeMs highlightStartTime(not_null<const HistoryItem*> item) const;
|
||||||
|
|
||||||
MessageIdsList getSelectedItems() const;
|
MessageIdsList getSelectedItems() const;
|
||||||
|
@ -673,7 +673,7 @@ private:
|
||||||
// Counts scrollTop for placing the scroll right at the unread
|
// Counts scrollTop for placing the scroll right at the unread
|
||||||
// messages bar, choosing from _history and _migrated unreadBar.
|
// messages bar, choosing from _history and _migrated unreadBar.
|
||||||
int unreadBarTop() const;
|
int unreadBarTop() const;
|
||||||
int itemTopForHighlight(not_null<HistoryItem*> item) const;
|
int itemTopForHighlight(not_null<HistoryView::Element*> view) const;
|
||||||
void scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId);
|
void scrollToCurrentVoiceMessage(FullMsgId fromId, FullMsgId toId);
|
||||||
|
|
||||||
// Scroll to current y without updating the _lastUserScrolled time.
|
// Scroll to current y without updating the _lastUserScrolled time.
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
|
|
||||||
HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item)
|
HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item)
|
||||||
: itemId(item->fullId()) {
|
: itemId(item->fullId()) {
|
||||||
|
@ -31,3 +32,21 @@ HistoryTextState::HistoryTextState(
|
||||||
: itemId(item->fullId())
|
: itemId(item->fullId())
|
||||||
, link(link) {
|
, link(link) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HistoryTextState::HistoryTextState(
|
||||||
|
not_null<const HistoryView::Element*> view)
|
||||||
|
: HistoryTextState(view->data()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryTextState::HistoryTextState(
|
||||||
|
not_null<const HistoryView::Element*> view,
|
||||||
|
const Text::StateResult &state)
|
||||||
|
: HistoryTextState(view->data(), state) {
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryTextState::HistoryTextState(
|
||||||
|
not_null<const HistoryView::Element*> view,
|
||||||
|
ClickHandlerPtr link)
|
||||||
|
: HistoryTextState(view->data(), link) {
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
class Element;
|
||||||
|
} // namespace HistoryView
|
||||||
|
|
||||||
class HistoryItem;
|
class HistoryItem;
|
||||||
|
|
||||||
enum HistoryCursorState {
|
enum HistoryCursorState {
|
||||||
|
@ -25,6 +29,13 @@ struct HistoryTextState {
|
||||||
HistoryTextState(
|
HistoryTextState(
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
ClickHandlerPtr link);
|
ClickHandlerPtr link);
|
||||||
|
HistoryTextState(not_null<const HistoryView::Element*> view);
|
||||||
|
HistoryTextState(
|
||||||
|
not_null<const HistoryView::Element*> view,
|
||||||
|
const Text::StateResult &state);
|
||||||
|
HistoryTextState(
|
||||||
|
not_null<const HistoryView::Element*> view,
|
||||||
|
ClickHandlerPtr link);
|
||||||
HistoryTextState(
|
HistoryTextState(
|
||||||
std::nullptr_t,
|
std::nullptr_t,
|
||||||
const Text::StateResult &state)
|
const Text::StateResult &state)
|
||||||
|
@ -56,7 +67,7 @@ struct HistoryStateRequest {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InfoDisplayType {
|
enum InfoDisplayType : char {
|
||||||
InfoDisplayDefault,
|
InfoDisplayDefault,
|
||||||
InfoDisplayOverImage,
|
InfoDisplayOverImage,
|
||||||
InfoDisplayOverBackground,
|
InfoDisplayOverBackground,
|
||||||
|
|
|
@ -8,10 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
#include "history/history_media.h"
|
#include "history/history_media.h"
|
||||||
|
#include "history/history_media_grouped.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -20,15 +24,51 @@ namespace {
|
||||||
constexpr int kAttachMessageToPreviousSecondsDelta = 900;
|
constexpr int kAttachMessageToPreviousSecondsDelta = 900;
|
||||||
|
|
||||||
} // namespace
|
} // 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<HistoryItem*> data, Context context)
|
Element::Element(not_null<HistoryItem*> data, Context context)
|
||||||
: _data(data)
|
: _data(data)
|
||||||
|
, _media(_data->media() ? _data->media()->createView(this) : nullptr)
|
||||||
, _context(context) {
|
, _context(context) {
|
||||||
|
Auth().data().registerItemView(this);
|
||||||
|
initGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<HistoryItem*> Element::data() const {
|
not_null<HistoryItem*> Element::data() const {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HistoryMedia *Element::media() const {
|
||||||
|
return _media.get();
|
||||||
|
}
|
||||||
|
|
||||||
Context Element::context() const {
|
Context Element::context() const {
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +84,7 @@ void Element::setY(int y) {
|
||||||
int Element::marginTop() const {
|
int Element::marginTop() const {
|
||||||
const auto item = data();
|
const auto item = data();
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
if (!item->isHiddenByGroup()) {
|
if (!isHiddenByGroup()) {
|
||||||
if (isAttachedToPrevious()) {
|
if (isAttachedToPrevious()) {
|
||||||
result += st::msgMarginTopAttached;
|
result += st::msgMarginTopAttached;
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,7 +100,11 @@ int Element::marginTop() const {
|
||||||
|
|
||||||
int Element::marginBottom() const {
|
int Element::marginBottom() const {
|
||||||
const auto item = data();
|
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() {
|
void Element::setPendingResize() {
|
||||||
|
@ -82,6 +126,113 @@ bool Element::isAttachedToNext() const {
|
||||||
return _flags & Flag::AttachedToNext;
|
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<Element*> leader) {
|
||||||
|
Expects(leader != this);
|
||||||
|
|
||||||
|
const auto group = Get<Group>();
|
||||||
|
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<not_null<Element*>> &&others) {
|
||||||
|
const auto group = Get<Group>();
|
||||||
|
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<Group>()) {
|
||||||
|
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<Group>()) {
|
||||||
|
if (group->leader == this) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
return group->leader->Get<Group>();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Element::initGroup() {
|
||||||
|
if (const auto groupId = _data->groupId()) {
|
||||||
|
AddComponents(Group::Bit());
|
||||||
|
const auto group = Get<Group>();
|
||||||
|
group->groupId = groupId;
|
||||||
|
group->leader = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Element::resetGroupMedia(
|
||||||
|
const std::vector<not_null<Element*>> &others) {
|
||||||
|
if (!others.empty()) {
|
||||||
|
_media = std::make_unique<HistoryGroupedMedia>(this, others);
|
||||||
|
} else if (_media) {
|
||||||
|
_media = _media->takeLastFromGroup();
|
||||||
|
}
|
||||||
|
Auth().data().requestViewResize(this);
|
||||||
|
}
|
||||||
|
|
||||||
void Element::previousInBlocksChanged() {
|
void Element::previousInBlocksChanged() {
|
||||||
recountDisplayDateInBlocks();
|
recountDisplayDateInBlocks();
|
||||||
recountAttachToPreviousInBlocks();
|
recountAttachToPreviousInBlocks();
|
||||||
|
@ -92,6 +243,17 @@ void Element::nextInBlocksChanged() {
|
||||||
setAttachToNext(false);
|
setAttachToNext(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Element::refreshDataId() {
|
||||||
|
if (const auto media = this->media()) {
|
||||||
|
media->refreshParentId(this);
|
||||||
|
if (const auto group = Get<Group>()) {
|
||||||
|
if (group->leader != this) {
|
||||||
|
group->leader->refreshDataId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
|
bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
|
||||||
const auto item = data();
|
const auto item = data();
|
||||||
if (!item->Has<HistoryMessageDate>() && !item->Has<HistoryMessageUnreadBar>()) {
|
if (!item->Has<HistoryMessageDate>() && !item->Has<HistoryMessageUnreadBar>()) {
|
||||||
|
@ -196,6 +358,53 @@ bool Element::displayFromName() const {
|
||||||
return false;
|
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() {
|
HistoryBlock *Element::block() {
|
||||||
return _block;
|
return _block;
|
||||||
}
|
}
|
||||||
|
@ -261,6 +470,29 @@ Element *Element::nextInBlocks() const {
|
||||||
return nullptr;
|
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(
|
void Element::clickHandlerActiveChanged(
|
||||||
const ClickHandlerPtr &handler,
|
const ClickHandlerPtr &handler,
|
||||||
bool active) {
|
bool active) {
|
||||||
|
@ -271,7 +503,7 @@ void Element::clickHandlerActiveChanged(
|
||||||
}
|
}
|
||||||
App::hoveredLinkItem(active ? this : nullptr);
|
App::hoveredLinkItem(active ? this : nullptr);
|
||||||
Auth().data().requestItemRepaint(_data);
|
Auth().data().requestItemRepaint(_data);
|
||||||
if (const auto media = _data->getMedia()) {
|
if (const auto media = this->media()) {
|
||||||
media->clickHandlerActiveChanged(handler, active);
|
media->clickHandlerActiveChanged(handler, active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,13 +518,13 @@ void Element::clickHandlerPressedChanged(
|
||||||
}
|
}
|
||||||
App::pressedLinkItem(pressed ? this : nullptr);
|
App::pressedLinkItem(pressed ? this : nullptr);
|
||||||
Auth().data().requestItemRepaint(_data);
|
Auth().data().requestItemRepaint(_data);
|
||||||
if (const auto media = _data->getMedia()) {
|
if (const auto media = this->media()) {
|
||||||
media->clickHandlerPressedChanged(handler, pressed);
|
media->clickHandlerPressedChanged(handler, pressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Element::~Element() {
|
Element::~Element() {
|
||||||
App::messageViewDestroyed(this);
|
Auth().data().unregisterItemView(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -13,11 +13,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
class HistoryBlock;
|
class HistoryBlock;
|
||||||
class HistoryItem;
|
class HistoryItem;
|
||||||
|
class HistoryMedia;
|
||||||
|
class HistoryWebPage;
|
||||||
struct HistoryTextState;
|
struct HistoryTextState;
|
||||||
struct HistoryStateRequest;
|
struct HistoryStateRequest;
|
||||||
|
enum InfoDisplayType : char;
|
||||||
|
|
||||||
namespace HistoryView {
|
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<Group, Element> {
|
||||||
|
MessageGroupId groupId = MessageGroupId::None;
|
||||||
|
Element *leader = nullptr;
|
||||||
|
std::vector<not_null<Element*>> others;
|
||||||
|
};
|
||||||
|
|
||||||
enum class Context : char {
|
enum class Context : char {
|
||||||
History,
|
History,
|
||||||
Feed,
|
Feed,
|
||||||
|
@ -26,7 +49,7 @@ enum class Context : char {
|
||||||
|
|
||||||
class Element
|
class Element
|
||||||
: public Object
|
: public Object
|
||||||
// , public RuntimeComposer
|
, public RuntimeComposer<Element>
|
||||||
, public ClickHandlerHost {
|
, public ClickHandlerHost {
|
||||||
public:
|
public:
|
||||||
Element(not_null<HistoryItem*> data, Context context);
|
Element(not_null<HistoryItem*> data, Context context);
|
||||||
|
@ -35,12 +58,15 @@ public:
|
||||||
NeedsResize = 0x01,
|
NeedsResize = 0x01,
|
||||||
AttachedToPrevious = 0x02,
|
AttachedToPrevious = 0x02,
|
||||||
AttachedToNext = 0x04,
|
AttachedToNext = 0x04,
|
||||||
|
HiddenByGroup = 0x08,
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||||
|
|
||||||
not_null<HistoryItem*> data() const;
|
not_null<HistoryItem*> data() const;
|
||||||
|
HistoryMedia *media() const;
|
||||||
Context context() const;
|
Context context() const;
|
||||||
|
void refreshDataId();
|
||||||
|
|
||||||
int y() const;
|
int y() const;
|
||||||
void setY(int y);
|
void setY(int y);
|
||||||
|
@ -49,10 +75,23 @@ public:
|
||||||
int marginBottom() const;
|
int marginBottom() const;
|
||||||
void setPendingResize();
|
void setPendingResize();
|
||||||
bool pendingResize() const;
|
bool pendingResize() const;
|
||||||
|
bool isUnderCursor() const;
|
||||||
|
|
||||||
bool isAttachedToPrevious() const;
|
bool isAttachedToPrevious() const;
|
||||||
bool isAttachedToNext() const;
|
bool isAttachedToNext() const;
|
||||||
|
|
||||||
|
int skipBlockWidth() const;
|
||||||
|
int skipBlockHeight() const;
|
||||||
|
QString skipBlock() const;
|
||||||
|
virtual int infoWidth() const;
|
||||||
|
|
||||||
|
bool isHiddenByGroup() const;
|
||||||
|
void makeGroupMember(not_null<Element*> leader);
|
||||||
|
void makeGroupLeader(std::vector<not_null<Element*>> &&others);
|
||||||
|
bool groupIdValidityChanged();
|
||||||
|
void validateGroupId();
|
||||||
|
Group *getFullGroup();
|
||||||
|
|
||||||
// For blocks context this should be called only from recountAttachToPreviousInBlocks().
|
// For blocks context this should be called only from recountAttachToPreviousInBlocks().
|
||||||
void setAttachToPrevious(bool attachToNext);
|
void setAttachToPrevious(bool attachToNext);
|
||||||
|
|
||||||
|
@ -75,6 +114,23 @@ public:
|
||||||
QPoint point,
|
QPoint point,
|
||||||
HistoryStateRequest request) const = 0;
|
HistoryStateRequest request) const = 0;
|
||||||
virtual void updatePressed(QPoint point) = 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.
|
// ClickHandlerHost interface.
|
||||||
void clickHandlerActiveChanged(
|
void clickHandlerActiveChanged(
|
||||||
|
@ -86,10 +142,25 @@ public:
|
||||||
|
|
||||||
// hasFromPhoto() returns true even if we don't display the photo
|
// 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
|
// but we need to skip a place at the left side for this photo
|
||||||
virtual bool displayFromPhoto() const;
|
|
||||||
virtual bool hasFromPhoto() const;
|
virtual bool hasFromPhoto() const;
|
||||||
|
virtual bool displayFromPhoto() const;
|
||||||
virtual bool hasFromName() const;
|
virtual bool hasFromName() const;
|
||||||
virtual bool displayFromName() 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.
|
// Legacy blocks structure.
|
||||||
HistoryBlock *block();
|
HistoryBlock *block();
|
||||||
|
@ -126,7 +197,12 @@ private:
|
||||||
virtual QSize performCountOptimalSize() = 0;
|
virtual QSize performCountOptimalSize() = 0;
|
||||||
virtual QSize performCountCurrentSize(int newWidth) = 0;
|
virtual QSize performCountCurrentSize(int newWidth) = 0;
|
||||||
|
|
||||||
|
void initGroup();
|
||||||
|
void resetGroupMedia(const std::vector<not_null<Element*>> &others);
|
||||||
|
|
||||||
const not_null<HistoryItem*> _data;
|
const not_null<HistoryItem*> _data;
|
||||||
|
std::unique_ptr<HistoryMedia> _media;
|
||||||
|
|
||||||
int _y = 0;
|
int _y = 0;
|
||||||
Context _context;
|
Context _context;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/edit_participant_box.h"
|
#include "boxes/edit_participant_box.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -655,7 +656,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
TextWithEntities ListWidget::getSelectedText() const {
|
TextWithEntities ListWidget::getSelectedText() const {
|
||||||
return _selectedItem
|
return _selectedItem
|
||||||
? _selectedItem->data()->selectedText(_selectedText)
|
? _selectedItem->selectedText(_selectedText)
|
||||||
: TextWithEntities();
|
: TextWithEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,7 +826,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
const auto item = view ? view->data().get() : nullptr;
|
const auto item = view ? view->data().get() : nullptr;
|
||||||
const auto itemId = item ? item->fullId() : FullMsgId();
|
const auto itemId = item ? item->fullId() : FullMsgId();
|
||||||
bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg());
|
bool canDelete = item && item->canDelete() && (item->id > 0 || !item->serviceMsg());
|
||||||
bool canForward = item && item->canForward();
|
bool canForward = item && item->allowsForward();
|
||||||
|
|
||||||
auto msg = dynamic_cast<HistoryMessage*>(item);
|
auto msg = dynamic_cast<HistoryMessage*>(item);
|
||||||
if (isUponSelected > 0) {
|
if (isUponSelected > 0) {
|
||||||
|
@ -833,7 +834,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||||
} else {
|
} else {
|
||||||
if (item && !isUponSelected) {
|
if (item && !isUponSelected) {
|
||||||
auto mediaHasTextForCopy = false;
|
auto mediaHasTextForCopy = false;
|
||||||
if (auto media = (msg ? msg->getMedia() : nullptr)) {
|
if (auto media = view->media()) {
|
||||||
mediaHasTextForCopy = media->hasTextForCopy();
|
mediaHasTextForCopy = media->hasTextForCopy();
|
||||||
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
|
if (media->type() == MediaTypeWebPage && static_cast<HistoryWebPage*>(media)->attach()) {
|
||||||
media = static_cast<HistoryWebPage*>(media)->attach();
|
media = static_cast<HistoryWebPage*>(media)->attach();
|
||||||
|
@ -954,8 +955,8 @@ void ListWidget::showContextInFolder(not_null<DocumentData*> document) {
|
||||||
|
|
||||||
void ListWidget::openContextGif(FullMsgId itemId) {
|
void ListWidget::openContextGif(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (auto media = item->getMedia()) {
|
if (auto media = item->media()) {
|
||||||
if (auto document = media->getDocument()) {
|
if (auto document = media->document()) {
|
||||||
Messenger::Instance().showDocument(document, item);
|
Messenger::Instance().showDocument(document, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -964,13 +965,10 @@ void ListWidget::openContextGif(FullMsgId itemId) {
|
||||||
|
|
||||||
void ListWidget::copyContextText(FullMsgId itemId) {
|
void ListWidget::copyContextText(FullMsgId itemId) {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto view = viewForItem(item)) {
|
||||||
if (media->type() == MediaTypeSticker) {
|
setToClipboard(view->selectedText(FullSelection));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setToClipboard(item->selectedText(FullSelection));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::setToClipboard(
|
void ListWidget::setToClipboard(
|
||||||
|
@ -1243,7 +1241,7 @@ void ListWidget::updateSelected() {
|
||||||
}
|
}
|
||||||
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) };
|
||||||
if (_mouseSelectType != TextSelectType::Letters) {
|
if (_mouseSelectType != TextSelectType::Letters) {
|
||||||
selection = _mouseActionItem->data()->adjustSelection(selection, _mouseSelectType);
|
selection = _mouseActionItem->adjustSelection(selection, _mouseSelectType);
|
||||||
}
|
}
|
||||||
if (_selectedText != selection) {
|
if (_selectedText != selection) {
|
||||||
_selectedText = selection;
|
_selectedText = selection;
|
||||||
|
@ -1342,16 +1340,20 @@ void ListWidget::performDrag() {
|
||||||
// auto forwardMimeType = QString();
|
// auto forwardMimeType = QString();
|
||||||
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
// auto pressedMedia = static_cast<HistoryMedia*>(nullptr);
|
||||||
// if (auto pressedItem = App::pressedItem()) {
|
// if (auto pressedItem = App::pressedItem()) {
|
||||||
// pressedMedia = pressedItem->getMedia();
|
// pressedMedia = pressedItem->media();
|
||||||
// if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) {
|
// if (_mouseCursorState == HistoryInDateCursorState
|
||||||
// Auth().data().setMimeForwardIds(Auth().data().itemOrItsGroup(pressedItem));
|
// || (pressedMedia && pressedMedia->dragItem())) {
|
||||||
|
// Auth().data().setMimeForwardIds(
|
||||||
|
// Auth().data().itemOrItsGroup(pressedItem->data()));
|
||||||
// forwardMimeType = qsl("application/x-td-forward");
|
// forwardMimeType = qsl("application/x-td-forward");
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// if (auto pressedLnkItem = App::pressedLinkItem()) {
|
// if (auto pressedLnkItem = App::pressedLinkItem()) {
|
||||||
// if ((pressedMedia = pressedLnkItem->getMedia())) {
|
// if ((pressedMedia = pressedLnkItem->media())) {
|
||||||
// if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
|
// if (forwardMimeType.isEmpty()
|
||||||
// Auth().data().setMimeForwardIds({ 1, pressedLnkItem->fullId() });
|
// && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||||
|
// Auth().data().setMimeForwardIds(
|
||||||
|
// { 1, pressedLnkItem->fullId() });
|
||||||
// forwardMimeType = qsl("application/x-td-forward");
|
// forwardMimeType = qsl("application/x-td-forward");
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,9 +10,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
|
||||||
class HistoryMessage;
|
class HistoryMessage;
|
||||||
|
struct HistoryMessageEdited;
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
|
// Special type of Component for the channel actions log.
|
||||||
|
struct LogEntryOriginal
|
||||||
|
: public RuntimeComponent<LogEntryOriginal, Element> {
|
||||||
|
LogEntryOriginal();
|
||||||
|
LogEntryOriginal(LogEntryOriginal &&other);
|
||||||
|
LogEntryOriginal &operator=(LogEntryOriginal &&other);
|
||||||
|
~LogEntryOriginal();
|
||||||
|
|
||||||
|
std::unique_ptr<HistoryWebPage> page;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class Message : public Element {
|
class Message : public Element {
|
||||||
public:
|
public:
|
||||||
Message(not_null<HistoryMessage*> data, Context context);
|
Message(not_null<HistoryMessage*> data, Context context);
|
||||||
|
@ -27,20 +40,58 @@ public:
|
||||||
QPoint point,
|
QPoint point,
|
||||||
HistoryStateRequest request) const override;
|
HistoryStateRequest request) const override;
|
||||||
void updatePressed(QPoint point) 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
|
// 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
|
// but we need to skip a place at the left side for this photo
|
||||||
bool displayFromPhoto() const override;
|
|
||||||
bool hasFromPhoto() const override;
|
bool hasFromPhoto() const override;
|
||||||
|
bool displayFromPhoto() const override;
|
||||||
bool hasFromName() const override;
|
bool hasFromName() const override;
|
||||||
bool displayFromName() 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:
|
private:
|
||||||
not_null<HistoryMessage*> message() const;
|
not_null<HistoryMessage*> message() const;
|
||||||
|
|
||||||
|
void initLogEntryOriginal();
|
||||||
|
void refreshEditedBadge();
|
||||||
void fromNameUpdated(int width) const;
|
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 paintFromName(Painter &p, QRect &trect, bool selected) const;
|
||||||
void paintForwardedInfo(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;
|
void paintReplyInfo(Painter &p, QRect &trect, bool selected) const;
|
||||||
|
@ -78,6 +129,20 @@ private:
|
||||||
QSize performCountOptimalSize() override;
|
QSize performCountOptimalSize() override;
|
||||||
QSize performCountCurrentSize(int newWidth) 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
|
} // namespace HistoryView
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_abstract_structure.h"
|
#include "data/data_abstract_structure.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
#include "layout.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -309,7 +310,7 @@ QRect Service::countGeometry() const {
|
||||||
|
|
||||||
QSize Service::performCountCurrentSize(int newWidth) {
|
QSize Service::performCountCurrentSize(int newWidth) {
|
||||||
const auto item = message();
|
const auto item = message();
|
||||||
const auto media = item->getMedia();
|
const auto media = this->media();
|
||||||
|
|
||||||
auto newHeight = item->displayedDateHeight();
|
auto newHeight = item->displayedDateHeight();
|
||||||
if (auto unreadbar = item->Get<HistoryMessageUnreadBar>()) {
|
if (auto unreadbar = item->Get<HistoryMessageUnreadBar>()) {
|
||||||
|
@ -349,7 +350,7 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
||||||
|
|
||||||
QSize Service::performCountOptimalSize() {
|
QSize Service::performCountOptimalSize() {
|
||||||
const auto item = message();
|
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 maxWidth = item->_text.maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
|
||||||
auto minHeight = item->_text.minHeight();
|
auto minHeight = item->_text.minHeight();
|
||||||
|
@ -409,11 +410,11 @@ void Service::draw(
|
||||||
|
|
||||||
p.setTextPalette(st::serviceTextPalette);
|
p.setTextPalette(st::serviceTextPalette);
|
||||||
|
|
||||||
if (auto media = item->getMedia()) {
|
if (auto media = this->media()) {
|
||||||
height -= st::msgServiceMargin.top() + media->height();
|
height -= st::msgServiceMargin.top() + media->height();
|
||||||
auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
|
auto left = st::msgServiceMargin.left() + (g.width() - media->maxWidth()) / 2, top = st::msgServiceMargin.top() + height + st::msgServiceMargin.top();
|
||||||
p.translate(left, 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);
|
p.translate(-left, -top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +436,7 @@ void Service::draw(
|
||||||
|
|
||||||
bool Service::hasPoint(QPoint point) const {
|
bool Service::hasPoint(QPoint point) const {
|
||||||
const auto item = message();
|
const auto item = message();
|
||||||
const auto media = item->getMedia();
|
const auto media = this->media();
|
||||||
|
|
||||||
auto g = countGeometry();
|
auto g = countGeometry();
|
||||||
if (g.width() < 1) {
|
if (g.width() < 1) {
|
||||||
|
@ -456,7 +457,7 @@ bool Service::hasPoint(QPoint point) const {
|
||||||
|
|
||||||
HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) const {
|
HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) const {
|
||||||
const auto item = message();
|
const auto item = message();
|
||||||
const auto media = item->getMedia();
|
const auto media = this->media();
|
||||||
|
|
||||||
auto result = HistoryTextState(item);
|
auto result = HistoryTextState(item);
|
||||||
|
|
||||||
|
@ -504,4 +505,15 @@ HistoryTextState Service::getState(QPoint point, HistoryStateRequest request) co
|
||||||
void Service::updatePressed(QPoint point) {
|
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
|
} // namespace HistoryView
|
||||||
|
|
|
@ -27,6 +27,10 @@ public:
|
||||||
QPoint point,
|
QPoint point,
|
||||||
HistoryStateRequest request) const override;
|
HistoryStateRequest request) const override;
|
||||||
void updatePressed(QPoint point) override;
|
void updatePressed(QPoint point) override;
|
||||||
|
TextWithEntities selectedText(TextSelection selection) const override;
|
||||||
|
TextSelection adjustSelection(
|
||||||
|
TextSelection selection,
|
||||||
|
TextSelectType type) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
not_null<HistoryService*> message() const;
|
not_null<HistoryService*> message() const;
|
||||||
|
|
|
@ -9,7 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
#include "overview/overview_layout.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_item.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "window/themes/window_theme.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/peer_list_controllers.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
#include "data/data_session.h"
|
|
||||||
|
|
||||||
namespace Layout = Overview::Layout;
|
namespace Layout = Overview::Layout;
|
||||||
|
|
||||||
|
@ -837,14 +839,14 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto getPhoto = [&]() -> PhotoData* {
|
auto getPhoto = [&]() -> PhotoData* {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
return media->getPhoto();
|
return media->photo();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
auto getFile = [&]() -> DocumentData* {
|
auto getFile = [&]() -> DocumentData* {
|
||||||
if (auto media = item->getMedia()) {
|
if (auto media = item->media()) {
|
||||||
return media->getDocument();
|
return media->document();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
@ -878,7 +880,7 @@ std::unique_ptr<BaseLayout> ListWidget::createLayout(
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case Type::Link:
|
case Type::Link:
|
||||||
return std::make_unique<Link>(item, item->getMedia());
|
return std::make_unique<Link>(item, item->media());
|
||||||
case Type::RoundFile:
|
case Type::RoundFile:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1313,7 +1315,7 @@ void ListWidget::showContextMenu(
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
if (overSelected != SelectionState::NotOverSelectedItems) {
|
if (overSelected != SelectionState::NotOverSelectedItems) {
|
||||||
if (item->canForward()) {
|
if (item->allowsForward()) {
|
||||||
_contextMenu->addAction(
|
_contextMenu->addAction(
|
||||||
lang(lng_context_forward_msg),
|
lang(lng_context_forward_msg),
|
||||||
base::lambda_guarded(this, [this, universalId] {
|
base::lambda_guarded(this, [this, universalId] {
|
||||||
|
@ -1497,7 +1499,7 @@ bool ListWidget::changeItemSelection(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
iterator->second.canDelete = item->canDelete();
|
iterator->second.canDelete = item->canDelete();
|
||||||
iterator->second.canForward = item->canForward();
|
iterator->second.canForward = item->allowsForward();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return changeExisting(iterator);
|
return changeExisting(iterator);
|
||||||
|
|
|
@ -78,7 +78,9 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LayoutItemBase : public RuntimeComposer, public ClickHandlerHost {
|
class LayoutItemBase
|
||||||
|
: public RuntimeComposer<LayoutItemBase>
|
||||||
|
, public ClickHandlerHost {
|
||||||
public:
|
public:
|
||||||
LayoutItemBase() {
|
LayoutItemBase() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "ui/special_buttons.h"
|
#include "ui/special_buttons.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
|
@ -327,8 +328,8 @@ void MainWidget::checkCurrentFloatPlayer() {
|
||||||
last->widget->detach();
|
last->widget->detach();
|
||||||
}
|
}
|
||||||
if (auto item = App::histItemById(fullId)) {
|
if (auto item = App::histItemById(fullId)) {
|
||||||
if (auto media = item->getMedia()) {
|
if (auto media = item->media()) {
|
||||||
if (auto document = media->getDocument()) {
|
if (auto document = media->document()) {
|
||||||
if (document->isVideoMessage()) {
|
if (document->isVideoMessage()) {
|
||||||
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](not_null<Float*> instance, bool visible) {
|
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](not_null<Float*> instance, bool visible) {
|
||||||
instance->hiddenByWidget = !visible;
|
instance->hiddenByWidget = !visible;
|
||||||
|
@ -4908,11 +4909,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||||
if (existing) {
|
if (existing) {
|
||||||
existing->destroy();
|
existing->destroy();
|
||||||
}
|
}
|
||||||
App::historyUnregItem(local);
|
|
||||||
Auth().messageIdChanging.notify({ local, newId }, true);
|
|
||||||
local->setRealId(d.vid.v);
|
local->setRealId(d.vid.v);
|
||||||
App::historyRegItem(local);
|
|
||||||
Auth().data().requestItemRepaint(local);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
App::historyUnregRandom(d.vrandom_id.v);
|
App::historyUnregRandom(d.vrandom_id.v);
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <rpl/merge.h>
|
#include <rpl/merge.h>
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "history/history_media.h"
|
#include "history/history_media.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "media/media_clip_reader.h"
|
#include "media/media_clip_reader.h"
|
||||||
|
@ -31,10 +32,10 @@ Float::Float(
|
||||||
, _item(item)
|
, _item(item)
|
||||||
, _toggleCallback(std::move(toggleCallback))
|
, _toggleCallback(std::move(toggleCallback))
|
||||||
, _draggedCallback(std::move(draggedCallback)) {
|
, _draggedCallback(std::move(draggedCallback)) {
|
||||||
auto media = _item->getMedia();
|
auto media = _item->media();
|
||||||
Assert(media != nullptr);
|
Assert(media != nullptr);
|
||||||
|
|
||||||
auto document = media->getDocument();
|
auto document = media->document();
|
||||||
Assert(document != nullptr);
|
Assert(document != nullptr);
|
||||||
Assert(document->isVideoMessage());
|
Assert(document->isVideoMessage());
|
||||||
|
|
||||||
|
@ -100,9 +101,10 @@ float64 Float::outRatio() const {
|
||||||
void Float::mouseReleaseEvent(QMouseEvent *e) {
|
void Float::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
if (_down) {
|
if (_down) {
|
||||||
_down = false;
|
_down = false;
|
||||||
if (auto media = _item ? _item->getMedia() : nullptr) {
|
// #TODO float player
|
||||||
media->playInline();
|
//if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||||
}
|
// media->playInline();
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
if (_drag) {
|
if (_drag) {
|
||||||
finishDrag(outRatio() < 0.5);
|
finishDrag(outRatio() < 0.5);
|
||||||
|
@ -119,9 +121,10 @@ void Float::finishDrag(bool closed) {
|
||||||
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
|
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||||
if (_item) {
|
if (_item) {
|
||||||
// Handle second click.
|
// Handle second click.
|
||||||
if (auto media = _item->getMedia()) {
|
// #TODO float player
|
||||||
media->playInline();
|
//if (auto media = _item->getMedia()) {
|
||||||
}
|
// media->playInline();
|
||||||
|
//}
|
||||||
Ui::showPeerHistoryAtItem(_item);
|
Ui::showPeerHistoryAtItem(_item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,13 +194,14 @@ void Float::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Clip::Reader *Float::getReader() const {
|
Clip::Reader *Float::getReader() const {
|
||||||
if (auto media = _item ? _item->getMedia() : nullptr) {
|
// #TODO float player
|
||||||
if (auto reader = media->getClipReader()) {
|
//if (auto media = _item ? _item->getMedia() : nullptr) {
|
||||||
if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
|
// if (auto reader = media->getClipReader()) {
|
||||||
return reader;
|
// if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
|
||||||
}
|
// return reader;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_media.h"
|
#include "data/data_media_types.h"
|
||||||
|
|
||||||
namespace Media {
|
namespace Media {
|
||||||
namespace Player {
|
namespace Player {
|
||||||
|
@ -222,8 +222,8 @@ bool Instance::moveInPlaylist(
|
||||||
}
|
}
|
||||||
const auto newIndex = *data->playlistIndex + delta;
|
const auto newIndex = *data->playlistIndex + delta;
|
||||||
if (const auto item = itemByIndex(data, newIndex)) {
|
if (const auto item = itemByIndex(data, newIndex)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto document = media->getDocument()) {
|
if (const auto document = media->document()) {
|
||||||
if (autonext) {
|
if (autonext) {
|
||||||
_switchToNextNotifier.notify({
|
_switchToNextNotifier.notify({
|
||||||
data->current,
|
data->current,
|
||||||
|
@ -300,11 +300,12 @@ void Instance::play(const AudioMsgId &audioId) {
|
||||||
documentLoadProgress(document);
|
documentLoadProgress(document);
|
||||||
}
|
}
|
||||||
} else if (document->isVideoMessage()) {
|
} else if (document->isVideoMessage()) {
|
||||||
if (const auto item = App::histItemById(audioId.contextId())) {
|
// #TODO float player
|
||||||
if (const auto media = item->getMedia()) {
|
//if (const auto item = App::histItemById(audioId.contextId())) {
|
||||||
media->playInline();
|
// if (const auto media = item->getMedia()) {
|
||||||
}
|
// media->playInline();
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,8 +430,8 @@ void Instance::preloadNext(not_null<Data*> data) {
|
||||||
}
|
}
|
||||||
const auto nextIndex = *data->playlistIndex + 1;
|
const auto nextIndex = *data->playlistIndex + 1;
|
||||||
if (const auto item = itemByIndex(data, nextIndex)) {
|
if (const auto item = itemByIndex(data, nextIndex)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto document = media->getDocument()) {
|
if (const auto document = media->document()) {
|
||||||
const auto isLoaded = document->loaded(
|
const auto isLoaded = document->loaded(
|
||||||
DocumentData::FilePathResolveSaveFromDataSilent);
|
DocumentData::FilePathResolveSaveFromDataSilent);
|
||||||
if (!isLoaded) {
|
if (!isLoaded) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "info/media/info_media_list_widget.h"
|
#include "info/media/info_media_list_widget.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_media.h"
|
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
@ -258,8 +258,8 @@ void Panel::refreshList() {
|
||||||
const auto contextId = current.contextId();
|
const auto contextId = current.contextId();
|
||||||
const auto peer = [&]() -> PeerData* {
|
const auto peer = [&]() -> PeerData* {
|
||||||
const auto item = contextId ? App::histItemById(contextId) : nullptr;
|
const auto item = contextId ? App::histItemById(contextId) : nullptr;
|
||||||
const auto media = item ? item->getMedia() : nullptr;
|
const auto media = item ? item->media() : nullptr;
|
||||||
const auto document = media ? media->getDocument() : nullptr;
|
const auto document = media ? media->document() : nullptr;
|
||||||
if (!document || !document->isSharedMediaMusic()) {
|
if (!document || !document->isSharedMediaMusic()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_user_photos.h"
|
#include "data/data_user_photos.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_media.h"
|
#include "history/history_media.h"
|
||||||
#include "styles/style_mediaview.h"
|
#include "styles/style_mediaview.h"
|
||||||
|
@ -473,10 +474,10 @@ auto GroupThumbs::createThumb(Key key) -> std::unique_ptr<Thumb> {
|
||||||
return createThumb(key, photo->date ? photo->thumb : ImagePtr());
|
return createThumb(key, photo->date ? photo->thumb : ImagePtr());
|
||||||
} else if (const auto msgId = base::get_if<FullMsgId>(&key)) {
|
} else if (const auto msgId = base::get_if<FullMsgId>(&key)) {
|
||||||
if (const auto item = App::histItemById(*msgId)) {
|
if (const auto item = App::histItemById(*msgId)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto photo = media->getPhoto()) {
|
if (const auto photo = media->photo()) {
|
||||||
return createThumb(key, photo->thumb);
|
return createThumb(key, photo->thumb);
|
||||||
} else if (const auto document = media->getDocument()) {
|
} else if (const auto document = media->document()) {
|
||||||
return createThumb(key, document->thumb);
|
return createThumb(key, document->thumb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "window/themes/window_theme_preview.h"
|
#include "window/themes/window_theme_preview.h"
|
||||||
#include "window/window_peer_menu.h"
|
#include "window/window_peer_menu.h"
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
|
@ -1236,13 +1237,13 @@ void MediaView::refreshMediaViewer() {
|
||||||
void MediaView::refreshCaption(HistoryItem *item) {
|
void MediaView::refreshCaption(HistoryItem *item) {
|
||||||
_caption = Text();
|
_caption = Text();
|
||||||
|
|
||||||
const auto media = item ? item->getMedia() : nullptr;
|
const auto media = item ? item->media() : nullptr;
|
||||||
if (!media) {
|
if (!media) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto caption = media->getCaption();
|
const auto caption = media->caption();
|
||||||
if (caption.text.isEmpty()) {
|
if (caption.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto asBot = [&] {
|
const auto asBot = [&] {
|
||||||
|
@ -1254,7 +1255,7 @@ void MediaView::refreshCaption(HistoryItem *item) {
|
||||||
_caption = Text(st::msgMinWidth);
|
_caption = Text(st::msgMinWidth);
|
||||||
_caption.setMarkedText(
|
_caption.setMarkedText(
|
||||||
st::mediaviewCaptionStyle,
|
st::mediaviewCaptionStyle,
|
||||||
caption,
|
{ caption, EntitiesInText() }, // #TODO caption entities parse
|
||||||
Ui::ItemTextOptions(item));
|
Ui::ItemTextOptions(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2448,10 +2449,10 @@ MediaView::Entity MediaView::entityForSharedMedia(int index) const {
|
||||||
|
|
||||||
MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const {
|
MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const {
|
||||||
if (const auto item = App::histItemById(itemId)) {
|
if (const auto item = App::histItemById(itemId)) {
|
||||||
if (const auto media = item->getMedia()) {
|
if (const auto media = item->media()) {
|
||||||
if (const auto photo = media->getPhoto()) {
|
if (const auto photo = media->photo()) {
|
||||||
return { photo, item };
|
return { photo, item };
|
||||||
} else if (const auto document = media->getDocument()) {
|
} else if (const auto document = media->document()) {
|
||||||
return { document, item };
|
return { document, item };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2474,7 +2475,7 @@ void MediaView::setContext(base::optional_variant<
|
||||||
not_null<PeerData*>> context) {
|
not_null<PeerData*>> context) {
|
||||||
if (auto item = base::get_if<not_null<HistoryItem*>>(&context)) {
|
if (auto item = base::get_if<not_null<HistoryItem*>>(&context)) {
|
||||||
_msgid = (*item)->fullId();
|
_msgid = (*item)->fullId();
|
||||||
_canForwardItem = (*item)->canForward();
|
_canForwardItem = (*item)->allowsForward();
|
||||||
_canDeleteItem = (*item)->canDelete();
|
_canDeleteItem = (*item)->canDelete();
|
||||||
_history = (*item)->history();
|
_history = (*item)->history();
|
||||||
_peer = _history->peer;
|
_peer = _history->peer;
|
||||||
|
|
|
@ -68,8 +68,8 @@ enum class MTPDmessage_ClientFlag : uint32 {
|
||||||
// message has an admin badge in supergroup
|
// message has an admin badge in supergroup
|
||||||
f_has_admin_badge = (1U << 20),
|
f_has_admin_badge = (1U << 20),
|
||||||
|
|
||||||
// message is not displayed because it is part of a group
|
//// message is not displayed because it is part of a group
|
||||||
f_hidden_by_group = (1U << 19),
|
//f_hidden_by_group = (1U << 19),
|
||||||
|
|
||||||
// update this when adding new client side flags
|
// update this when adding new client side flags
|
||||||
MIN_FIELD = (1U << 19),
|
MIN_FIELD = (1U << 19),
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
#include "styles/style_overview.h"
|
#include "styles/style_overview.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
#include "core/file_utilities.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 "media/player/media_player_instance.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history_media_types.h"
|
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "ui/effects/round_checkbox.h"
|
#include "ui/effects/round_checkbox.h"
|
||||||
#include "ui/text_options.h"
|
#include "ui/text_options.h"
|
||||||
|
@ -1165,7 +1165,7 @@ bool Document::updateStatusText() {
|
||||||
|
|
||||||
Link::Link(
|
Link::Link(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
HistoryMedia *media)
|
Data::Media *media)
|
||||||
: ItemBase(parent) {
|
: ItemBase(parent) {
|
||||||
AddComponents(Info::Bit());
|
AddComponents(Info::Bit());
|
||||||
|
|
||||||
|
@ -1211,9 +1211,10 @@ Link::Link(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_page = (media && media->type() == MediaTypeWebPage)
|
// #TODO webpage
|
||||||
? static_cast<HistoryWebPage*>(media)->webpage().get()
|
//_page = (media && media->type() == MediaTypeWebPage)
|
||||||
: nullptr;
|
// ? static_cast<HistoryWebPage*>(media)->webpage().get()
|
||||||
|
// : nullptr;
|
||||||
if (_page) {
|
if (_page) {
|
||||||
mainUrl = _page->url;
|
mainUrl = _page->url;
|
||||||
if (_page->document) {
|
if (_page->document) {
|
||||||
|
|
|
@ -20,6 +20,10 @@ namespace style {
|
||||||
struct RoundCheckbox;
|
struct RoundCheckbox;
|
||||||
} // namespace style
|
} // namespace style
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class Media;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Overview {
|
namespace Overview {
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
|
||||||
|
@ -172,7 +176,7 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Info : public RuntimeComponent<Info> {
|
struct Info : public RuntimeComponent<Info, LayoutItemBase> {
|
||||||
int top = 0;
|
int top = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -325,7 +329,7 @@ class Link : public ItemBase {
|
||||||
public:
|
public:
|
||||||
Link(
|
Link(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
HistoryMedia *media);
|
Data::Media *media);
|
||||||
|
|
||||||
void initDimensions() override;
|
void initDimensions() override;
|
||||||
int32 resizeGetHeight(int32 width) override;
|
int32 resizeGetHeight(int32 width) override;
|
||||||
|
|
|
@ -2698,24 +2698,35 @@ bool Text::hasSkipBlock() const {
|
||||||
return _blocks.empty() ? false : _blocks.back()->type() == TextBlockTSkip;
|
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) {
|
if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) {
|
||||||
auto block = static_cast<SkipBlock*>(_blocks.back().get());
|
const auto block = static_cast<SkipBlock*>(_blocks.back().get());
|
||||||
if (block->width() == width && block->height() == height) return;
|
if (block->width() == width && block->height() == height) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
_text.resize(block->from());
|
_text.resize(block->from());
|
||||||
_blocks.pop_back();
|
_blocks.pop_back();
|
||||||
}
|
}
|
||||||
_text.push_back('_');
|
_text.push_back('_');
|
||||||
_blocks.push_back(std::make_unique<SkipBlock>(_st->font, _text, _text.size() - 1, width, height, 0));
|
_blocks.push_back(std::make_unique<SkipBlock>(
|
||||||
|
_st->font,
|
||||||
|
_text,
|
||||||
|
_text.size() - 1,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
0));
|
||||||
recountNaturalSize(false);
|
recountNaturalSize(false);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::removeSkipBlock() {
|
bool Text::removeSkipBlock() {
|
||||||
if (!_blocks.empty() && _blocks.back()->type() == TextBlockTSkip) {
|
if (_blocks.empty() || _blocks.back()->type() != TextBlockTSkip) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
_text.resize(_blocks.back()->from());
|
_text.resize(_blocks.back()->from());
|
||||||
_blocks.pop_back();
|
_blocks.pop_back();
|
||||||
recountNaturalSize(false);
|
recountNaturalSize(false);
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Text::countWidth(int width) const {
|
int Text::countWidth(int width) const {
|
||||||
|
|
|
@ -89,8 +89,8 @@ public:
|
||||||
bool hasLinks() const;
|
bool hasLinks() const;
|
||||||
|
|
||||||
bool hasSkipBlock() const;
|
bool hasSkipBlock() const;
|
||||||
void setSkipBlock(int32 width, int32 height);
|
bool updateSkipBlock(int width, int height);
|
||||||
void removeSkipBlock();
|
bool removeSkipBlock();
|
||||||
|
|
||||||
int32 maxWidth() const {
|
int32 maxWidth() const {
|
||||||
return _maxWidth.ceil().toInt();
|
return _maxWidth.ceil().toInt();
|
||||||
|
|
|
@ -427,13 +427,24 @@ void Manager::notificationReplied(
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeManager::doShowNotification(HistoryItem *item, int forwardedCount) {
|
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;
|
const auto title = options.hideNameAndPhoto ? qsl("Telegram Desktop") : item->history()->peer->name;
|
||||||
QString subtitle = options.hideNameAndPhoto ? QString() : item->notificationHeader();
|
const auto 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 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;
|
System::~System() = default;
|
||||||
|
|
|
@ -173,6 +173,10 @@
|
||||||
<(src_loc)/data/data_feed_messages.h
|
<(src_loc)/data/data_feed_messages.h
|
||||||
<(src_loc)/data/data_flags.h
|
<(src_loc)/data/data_flags.h
|
||||||
<(src_loc)/data/data_game.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.cpp
|
||||||
<(src_loc)/data/data_messages.h
|
<(src_loc)/data/data_messages.h
|
||||||
<(src_loc)/data/data_notify_settings.cpp
|
<(src_loc)/data/data_notify_settings.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue