tdesktop/Telegram/SourceFiles/data/data_photo.cpp

429 lines
10 KiB
C++
Raw Normal View History

/*
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_photo.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "data/data_reply_preview.h"
2020-05-25 14:16:04 +00:00
#include "data/data_photo_media.h"
#include "ui/image/image.h"
#include "ui/image/image_source.h"
#include "main/main_session.h"
#include "mainwidget.h"
2020-05-25 14:16:04 +00:00
#include "storage/file_download.h"
#include "core/application.h"
#include "facades.h"
#include "app.h"
2020-05-25 14:16:04 +00:00
namespace {
constexpr auto kPhotoSideLimit = 1280;
2020-05-25 14:16:04 +00:00
using Data::PhotoMedia;
using Data::PhotoSize;
using Data::PhotoSizeIndex;
using Data::kPhotoSizeCount;
} // namespace
PhotoData::PhotoData(not_null<Data::Session*> owner, PhotoId id)
: id(id)
, _owner(owner) {
2018-07-13 16:49:46 +00:00
}
2020-05-25 14:16:04 +00:00
PhotoData::~PhotoData() {
for (auto &image : _images) {
base::take(image.loader).reset();
}
}
Data::Session &PhotoData::owner() const {
return *_owner;
}
2019-07-24 11:45:24 +00:00
Main::Session &PhotoData::session() const {
return _owner->session();
}
2020-05-25 14:16:04 +00:00
void PhotoData::automaticLoadSettingsChanged() {
const auto index = PhotoSizeIndex(PhotoSize::Large);
if (!(_images[index].flags & Data::CloudFile::Flag::Cancelled)) {
2020-05-25 14:16:04 +00:00
return;
}
_images[index].loader = nullptr;
_images[index].flags &= ~Data::CloudFile::Flag::Cancelled;
2020-05-25 14:16:04 +00:00
}
void PhotoData::load(
2018-07-13 21:25:47 +00:00
Data::FileOrigin origin,
2020-05-25 14:16:04 +00:00
LoadFromCloudSetting fromCloud,
bool autoLoading) {
load(PhotoSize::Large, origin, fromCloud, autoLoading);
}
2020-05-25 14:16:04 +00:00
bool PhotoData::loading() const {
return loading(PhotoSize::Large);
}
int PhotoData::validSizeIndex(PhotoSize size) const {
const auto index = PhotoSizeIndex(size);
for (auto i = index; i != kPhotoSizeCount; ++i) {
if (_images[i].location.valid()) {
if (i != index) {
int a = 0; AssertIsDebug();
}
return i;
}
}
return PhotoSizeIndex(PhotoSize::Large);
}
bool PhotoData::hasExact(PhotoSize size) const {
return _images[PhotoSizeIndex(size)].location.valid();
}
2020-05-25 14:16:04 +00:00
bool PhotoData::loading(PhotoSize size) const {
return (_images[validSizeIndex(size)].loader != nullptr);
}
2020-05-25 14:16:04 +00:00
bool PhotoData::failed(PhotoSize size) const {
const auto flags = _images[validSizeIndex(size)].flags;
return (flags & Data::CloudFile::Flag::Failed);
}
2020-05-25 14:16:04 +00:00
const ImageLocation &PhotoData::location(PhotoSize size) const {
return _images[validSizeIndex(size)].location;
}
int PhotoData::SideLimit() {
return kPhotoSideLimit;
}
std::optional<QSize> PhotoData::size(PhotoSize size) const {
const auto &provided = location(size);
const auto result = QSize{ provided.width(), provided.height() };
const auto limit = SideLimit();
if (result.isEmpty()) {
return std::nullopt;
} else if (result.width() <= limit && result.height() <= limit) {
return result;
}
const auto scaled = result.scaled(limit, limit, Qt::KeepAspectRatio);
return QSize(std::max(scaled.width(), 1), std::max(scaled.height(), 1));
2020-05-25 14:16:04 +00:00
}
int PhotoData::imageByteSize(PhotoSize size) const {
return _images[validSizeIndex(size)].byteSize;
}
bool PhotoData::displayLoading() const {
2020-05-25 14:16:04 +00:00
const auto index = PhotoSizeIndex(PhotoSize::Large);
return _images[index].loader
? (!_images[index].loader->loadingLocal()
|| !_images[index].loader->autoLoading())
: (uploading() && !waitingForAlbum());
}
void PhotoData::cancel() {
2020-05-25 14:16:04 +00:00
if (!loading()) {
return;
}
const auto index = PhotoSizeIndex(PhotoSize::Large);
_images[index].flags |= Data::CloudFile::Flag::Cancelled;
2020-05-25 14:16:04 +00:00
destroyLoader(PhotoSize::Large);
_owner->photoLoadDone(this);
}
float64 PhotoData::progress() const {
if (uploading()) {
if (uploadingData->size > 0) {
2020-05-25 14:16:04 +00:00
const auto result = float64(uploadingData->offset)
/ uploadingData->size;
return snap(result, 0., 1.);
}
2020-05-25 14:16:04 +00:00
return 0.;
}
2020-05-25 14:16:04 +00:00
const auto index = PhotoSizeIndex(PhotoSize::Large);
return loading() ? _images[index].loader->currentProgress() : 0.;
}
bool PhotoData::cancelled() const {
const auto index = PhotoSizeIndex(PhotoSize::Large);
return (_images[index].flags & Data::CloudFile::Flag::Cancelled);
}
void PhotoData::setWaitingForAlbum() {
if (uploading()) {
uploadingData->waitingForAlbum = true;
}
}
bool PhotoData::waitingForAlbum() const {
return uploading() && uploadingData->waitingForAlbum;
}
int32 PhotoData::loadOffset() const {
2020-05-25 14:16:04 +00:00
const auto index = PhotoSizeIndex(PhotoSize::Large);
return loading() ? _images[index].loader->currentOffset() : 0;
}
bool PhotoData::uploading() const {
return (uploadingData != nullptr);
}
void PhotoData::unload() {
_replyPreview = nullptr;
}
Image *PhotoData::getReplyPreview(Data::FileOrigin origin) {
if (!_replyPreview) {
_replyPreview = std::make_unique<Data::ReplyPreview>(this);
}
return _replyPreview->image(origin);
}
void PhotoData::setRemoteLocation(
int32 dc,
uint64 access,
const QByteArray &fileReference) {
_fileReference = fileReference;
if (_dc != dc || _access != access) {
_dc = dc;
_access = access;
}
}
2018-07-13 16:49:46 +00:00
MTPInputPhoto PhotoData::mtpInput() const {
return MTP_inputPhoto(
MTP_long(id),
MTP_long(_access),
MTP_bytes(_fileReference));
}
QByteArray PhotoData::fileReference() const {
return _fileReference;
}
void PhotoData::refreshFileReference(const QByteArray &value) {
_fileReference = value;
2020-05-25 14:16:04 +00:00
for (auto &image : _images) {
image.location.refreshFileReference(value);
}
2018-07-13 16:49:46 +00:00
}
void PhotoData::collectLocalData(not_null<PhotoData*> local) {
if (local == this) {
return;
}
2020-05-25 14:16:04 +00:00
for (auto i = 0; i != kPhotoSizeCount; ++i) {
if (const auto from = local->_images[i].location.file().cacheKey()) {
if (const auto to = _images[i].location.file().cacheKey()) {
_owner->cache().copyIfEmpty(from, to);
}
}
2020-05-25 14:16:04 +00:00
}
if (const auto localMedia = local->activeMediaView()) {
const auto media = createMediaView();
media->collectLocalData(localMedia.get());
// Keep DocumentMedia alive for some more time.
// NB! This allows DocumentMedia to outlive Main::Session!
// In case this is a problem this code should be rewritten.
crl::on_main(&session(), [media] {});
}
}
bool PhotoData::isNull() const {
2020-05-25 14:16:04 +00:00
return !_images[PhotoSizeIndex(PhotoSize::Large)].location.valid();
}
2020-05-25 14:16:04 +00:00
void PhotoData::load(
PhotoSize size,
Data::FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading) {
const auto index = validSizeIndex(size);
2020-05-25 14:16:04 +00:00
auto &image = _images[index];
if (image.loader) {
if (fromCloud == LoadFromCloudOrLocal) {
image.loader->permitLoadFromCloud();
}
return;
} else if ((image.flags & Data::CloudFile::Flag::Failed)
2020-05-25 14:16:04 +00:00
|| !image.location.valid()) {
return;
} else if (const auto active = activeMediaView()) {
if (active->image(size)) {
return;
}
}
// Could've changed, if the requested size didn't have a location.
size = static_cast<PhotoSize>(index);
image.flags &= ~Data::CloudFile::Flag::Cancelled;
2020-05-25 14:16:04 +00:00
image.loader = CreateFileLoader(
image.location.file(),
origin,
QString(),
image.byteSize,
UnknownFileLocation,
LoadToCacheAsWell,
fromCloud,
autoLoading,
Data::kImageCacheTag);
image.loader->updates(
) | rpl::start_with_next_error_done([=] {
if (size == PhotoSize::Large) {
_owner->photoLoadProgress(this);
}
}, [=, &image](bool started) {
finishLoad(size);
image.flags |= Data::CloudFile::Flag::Failed;
2020-05-25 14:16:04 +00:00
if (size == PhotoSize::Large) {
_owner->photoLoadFail(this, started);
}
}, [=, &image] {
finishLoad(size);
if (size == PhotoSize::Large) {
_owner->photoLoadDone(this);
}
}, image.loader->lifetime());
2020-05-25 14:16:04 +00:00
image.loader->start();
2020-05-25 14:16:04 +00:00
if (size == PhotoSize::Large) {
_owner->notifyPhotoLayoutChanged(this);
}
}
2020-05-25 14:16:04 +00:00
void PhotoData::finishLoad(PhotoSize size) {
const auto index = PhotoSizeIndex(size);
auto &image = _images[index];
// NB! image.loader may be in ~FileLoader() already.
const auto guard = gsl::finally([&] {
destroyLoader(size);
});
if (!image.loader || image.loader->cancelled()) {
image.flags |= Data::CloudFile::Flag::Cancelled;
2020-05-25 14:16:04 +00:00
return;
} else if (auto read = image.loader->imageData(); read.isNull()) {
image.flags |= Data::CloudFile::Flag::Failed;
2020-05-25 14:16:04 +00:00
} else if (const auto active = activeMediaView()) {
active->set(size, std::move(read));
}
}
2020-05-25 14:16:04 +00:00
void PhotoData::destroyLoader(PhotoSize size) {
const auto index = PhotoSizeIndex(size);
auto &image = _images[index];
// NB! image.loader may be in ~FileLoader() already.
if (!image.loader) {
return;
}
const auto loader = base::take(image.loader);
if (image.flags & Data::CloudFile::Flag::Cancelled) {
2020-05-25 14:16:04 +00:00
loader->cancel();
}
}
2020-05-25 14:16:04 +00:00
std::shared_ptr<PhotoMedia> PhotoData::createMediaView() {
if (auto result = activeMediaView()) {
return result;
}
auto result = std::make_shared<PhotoMedia>(this);
_media = result;
return result;
}
2020-05-25 14:16:04 +00:00
std::shared_ptr<PhotoMedia> PhotoData::activeMediaView() const {
return _media.lock();
}
void PhotoData::updateImages(
2020-05-25 14:16:04 +00:00
const QByteArray &inlineThumbnailBytes,
const ImageWithLocation &small,
const ImageWithLocation &thumbnail,
const ImageWithLocation &large) {
if (!inlineThumbnailBytes.isEmpty()
&& _inlineThumbnailBytes.isEmpty()) {
_inlineThumbnailBytes = inlineThumbnailBytes;
}
2020-05-25 14:16:04 +00:00
const auto update = [&](PhotoSize size, const ImageWithLocation &data) {
Data::UpdateCloudFile(
_images[PhotoSizeIndex(size)],
data,
owner().cache(),
[=](Data::FileOrigin origin) { load(size, origin); },
[=](QImage preloaded) {
if (const auto media = activeMediaView()) {
media->set(size, data.preloaded);
}
});
};
2020-05-25 14:16:04 +00:00
update(PhotoSize::Small, small);
update(PhotoSize::Thumbnail, thumbnail);
update(PhotoSize::Large, large);
}
int PhotoData::width() const {
2020-05-25 14:16:04 +00:00
return _images[PhotoSizeIndex(PhotoSize::Large)].location.width();
}
int PhotoData::height() const {
2020-05-25 14:16:04 +00:00
return _images[PhotoSizeIndex(PhotoSize::Large)].location.height();
}
PhotoClickHandler::PhotoClickHandler(
not_null<PhotoData*> photo,
FullMsgId context,
PeerData *peer)
: FileClickHandler(context)
, _session(&photo->session())
, _photo(photo)
, _peer(peer) {
}
void PhotoOpenClickHandler::onClickImpl() const {
if (valid()) {
Core::App().showPhoto(this);
}
}
void PhotoSaveClickHandler::onClickImpl() const {
if (!valid()) {
return;
}
const auto data = photo();
if (!data->date) {
return;
} else {
2020-05-25 14:16:04 +00:00
data->load(context());
}
}
void PhotoCancelClickHandler::onClickImpl() const {
if (!valid()) {
return;
}
const auto data = photo();
if (!data->date) {
return;
} else if (data->uploading()) {
2019-04-25 12:45:15 +00:00
if (const auto item = data->owner().message(context())) {
App::main()->cancelUploadLayer(item);
}
} else {
data->cancel();
}
}