From 4ed2d75c7416680517fe05c32d0c273295aa3e08 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 24 Dec 2015 22:26:28 +0300 Subject: [PATCH] upload radial progress and upload cancel done, photo / file / audio autodownload settings respected --- Telegram/Resources/lang.strings | 1 + Telegram/SourceFiles/app.cpp | 8 +- Telegram/SourceFiles/audio.cpp | 9 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 12 +- Telegram/SourceFiles/dropdown.cpp | 26 +- Telegram/SourceFiles/facades.cpp | 4 +- Telegram/SourceFiles/fileuploader.cpp | 37 +- Telegram/SourceFiles/fileuploader.h | 5 +- Telegram/SourceFiles/gui/images.cpp | 196 ++-- Telegram/SourceFiles/gui/images.h | 154 ++- Telegram/SourceFiles/history.cpp | 383 ++++---- Telegram/SourceFiles/history.h | 190 ++-- Telegram/SourceFiles/historywidget.cpp | 13 +- Telegram/SourceFiles/layerwidget.cpp | 13 +- Telegram/SourceFiles/layout.cpp | 96 +- Telegram/SourceFiles/layout.h | 13 +- Telegram/SourceFiles/localimageloader.h | 9 +- Telegram/SourceFiles/localstorage.cpp | 27 +- Telegram/SourceFiles/localstorage.h | 4 +- Telegram/SourceFiles/mainwidget.cpp | 144 +-- Telegram/SourceFiles/mediaview.cpp | 224 ++--- Telegram/SourceFiles/mediaview.h | 10 +- .../SourceFiles/mtproto/mtpFileLoader.cpp | 453 ++++----- Telegram/SourceFiles/mtproto/mtpFileLoader.h | 77 +- Telegram/SourceFiles/overviewwidget.cpp | 11 +- Telegram/SourceFiles/playerwidget.cpp | 12 +- Telegram/SourceFiles/settings.h | 2 +- Telegram/SourceFiles/settingswidget.cpp | 138 +-- Telegram/SourceFiles/settingswidget.h | 12 +- Telegram/SourceFiles/structs.cpp | 914 ++++++++++++++---- Telegram/SourceFiles/structs.h | 367 ++++--- Telegram/SourceFiles/types.h | 5 + 32 files changed, 2011 insertions(+), 1558 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index edf3ff2c6..9695dc1d3 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -593,6 +593,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org "lng_emoji_category7" = "Symbols & Flags"; "lng_switch_stickers" = "Stickers"; +"lng_switch_stickers_gifs" = "GIFs & Stickers"; "lng_switch_emoji" = "Emoji"; "lng_box_remove" = "Remove"; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 49e8cab33..5efb247a9 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1176,7 +1176,7 @@ namespace App { case 'm': newThumbLevel = 2; newMediumLevel = 0; newFullLevel = 3; break; // box 320x320 case 'x': newThumbLevel = 5; newMediumLevel = 3; newFullLevel = 1; break; // box 800x800 case 'y': newThumbLevel = 6; newMediumLevel = 6; newFullLevel = 0; break; // box 1280x1280 - case 'w': newThumbLevel = 8; newMediumLevel = 8; newFullLevel = 2; break; // box 2560x2560 + case 'w': newThumbLevel = 8; newMediumLevel = 8; newFullLevel = 2; break; // box 2560x2560 // if loading this fix HistoryPhoto::updateFrom case 'a': newThumbLevel = 1; newMediumLevel = 4; newFullLevel = 8; break; // crop 160x160 case 'b': newThumbLevel = 3; newMediumLevel = 1; newFullLevel = 7; break; // crop 320x320 case 'c': newThumbLevel = 4; newMediumLevel = 2; newFullLevel = 6; break; // crop 640x640 @@ -1431,6 +1431,8 @@ namespace App { photosData.erase(i); } convert->id = photo; + delete convert->uploadingData; + convert->uploadingData = 0; } convert->access = access; if (!convert->date && date) { @@ -1624,7 +1626,7 @@ namespace App { } } } - if (convert->sticker() && !convert->sticker()->loc.dc && thumbLocation.dc) { + if (convert->sticker() && convert->sticker()->loc.isNull() && !thumbLocation.isNull()) { convert->sticker()->loc = thumbLocation; } @@ -1671,7 +1673,7 @@ namespace App { } } } - if (result->sticker() && !result->sticker()->loc.dc && thumbLocation.dc) { + if (result->sticker() && result->sticker()->loc.isNull() && !thumbLocation.isNull()) { result->sticker()->loc = thumbLocation; } } diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp index 098d01ebb..fb5e7984a 100644 --- a/Telegram/SourceFiles/audio.cpp +++ b/Telegram/SourceFiles/audio.cpp @@ -455,7 +455,7 @@ void AudioPlayer::play(const AudioMsgId &audio, int64 position) { } current->audio = audio; current->file = audio.audio->location(true); - current->data = audio.audio->data; + current->data = audio.audio->data(); if (current->file.isEmpty() && current->data.isEmpty()) { setStoppedState(current, AudioPlayerStoppedAtError); onError(audio); @@ -499,14 +499,11 @@ void AudioPlayer::play(const SongMsgId &song, int64 position) { } current->song = song; current->file = song.song->location(true); - current->data = song.song->data; + current->data = song.song->data(); if (current->file.isEmpty() && current->data.isEmpty()) { setStoppedState(current); - if (!song.song->loader) { + if (!song.song->loading()) { DocumentOpenLink::doOpen(song.song); - song.song->openOnSave = 0; - song.song->openOnSaveMsgId = FullMsgId(); - if (song.song->loader) song.song->loader->start(true, true); } } else { current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying; diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index f8ea8d119..d3f235ff8 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -147,16 +147,14 @@ void StickerSetInner::paintEvent(QPaintEvent *e) { if (goodThumb) { doc->thumb->load(); } else { - bool already = !doc->already().isEmpty(), hasdata = !doc->data.isEmpty(); - if (!already && !hasdata && !doc->loader && doc->status == FileReady) { - doc->openOnSave = 0; - doc->save(QString()); + if (doc->status == FileReady) { + doc->automaticLoad(0); } - if (doc->sticker()->img->isNull() && (already || hasdata)) { - if (already) { + if (doc->sticker()->img->isNull() && doc->loaded() && doc->loaded(true)) { + if (doc->data().isEmpty()) { doc->sticker()->img = ImagePtr(doc->already()); } else { - doc->sticker()->img = ImagePtr(doc->data); + doc->sticker()->img = ImagePtr(doc->data()); } } } diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 6a22b5a0f..aeef5594e 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -1331,18 +1331,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { if (goodThumb) { sticker->thumb->load(); } else { - bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty(); - if (!already && !hasdata && !sticker->loader && sticker->status == FileReady) { - sticker->openOnSave = 0; - sticker->save(QString()); - } - if (sticker->sticker()->img->isNull() && (already || hasdata)) { - if (already) { - sticker->sticker()->img = ImagePtr(sticker->already()); - } else { - sticker->sticker()->img = ImagePtr(sticker->data); - } - } + sticker->checkSticker(); } float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height())); @@ -1521,18 +1510,7 @@ void StickerPanInner::preloadImages() { if (goodThumb) { sticker->thumb->load(); } else { - bool already = !sticker->already().isEmpty(), hasdata = !sticker->data.isEmpty(); - if (!already && !hasdata && !sticker->loader && sticker->status == FileReady) { - sticker->openOnSave = 0; - sticker->save(QString()); - } - //if (sticker->sticker->img->isNull() && (already || hasdata)) { - // if (already) { - // sticker->sticker->img = ImagePtr(sticker->already()); - // } else { - // sticker->sticker->img = ImagePtr(sticker->data); - // } - //} + sticker->automaticLoad(0); } } if (k > StickerPanPerRow * (StickerPanPerRow + 1)) break; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 00f38e7ed..411c1d89a 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -154,9 +154,7 @@ namespace Notify { if (HistoryMedia *media = item->getMedia()) { media->stopInline(item); if (DocumentData *document = media->getDocument()) { // forget data from memory - if (!document->data.isEmpty() && document->prepareAutoLoader(item)) { - document->data.clear(); - } + document->forget(); } stopped = true; } diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp index dd41f5725..2c5f30adc 100644 --- a/Telegram/SourceFiles/fileuploader.cpp +++ b/Telegram/SourceFiles/fileuploader.cpp @@ -46,7 +46,7 @@ void FileUploader::uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &me } else if (media.type == PrepareAudio) { AudioData *audio = App::feedAudio(media.audio); audio->status = FileUploading; - audio->data = media.data; + audio->setData(media.data); } queue.insert(msgId, File(media)); sendNext(); @@ -54,7 +54,8 @@ void FileUploader::uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &me void FileUploader::upload(const FullMsgId &msgId, const FileLoadResultPtr &file) { if (file->type == PreparePhoto) { - App::feedPhoto(file->photo, file->photoThumbs); + PhotoData *photo = App::feedPhoto(file->photo, file->photoThumbs); + photo->uploadingData = new PhotoData::UploadingData(file->partssize); } else if (file->type == PrepareDocument) { DocumentData *document; if (file->thumb.isNull()) { @@ -69,7 +70,7 @@ void FileUploader::upload(const FullMsgId &msgId, const FileLoadResultPtr &file) } else if (file->type == PrepareAudio) { AudioData *audio = App::feedAudio(file->audio); audio->status = FileUploading; - audio->data = file->content; + audio->setData(file->content); } queue.insert(msgId, File(file)); sendNext(); @@ -115,7 +116,7 @@ void FileUploader::killSessions() { } void FileUploader::sendNext() { - if (sentSize >= MaxUploadFileParallelSize) return; + if (sentSize >= MaxUploadFileParallelSize || _paused.msg) return; bool killing = killSessionsTimer.isActive(); if (queue.isEmpty()) { @@ -232,6 +233,15 @@ void FileUploader::cancel(const FullMsgId &msgId) { } } +void FileUploader::pause(const FullMsgId &msgId) { + _paused = msgId; +} + +void FileUploader::unpause() { + _paused = FullMsgId(); + sendNext(); +} + void FileUploader::confirm(const FullMsgId &msgId) { } @@ -274,21 +284,28 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { int32 dc = dcIt.value(); dcMap.erase(dcIt); + int32 sentPartSize = 0; Queue::const_iterator k = queue.constFind(uploading); if (i != requestsSent.cend()) { - sentSize -= i.value().size(); - sentSizes[dc] -= i.value().size(); + sentPartSize = i.value().size(); requestsSent.erase(i); } else { - sentSize -= k->docPartSize; - sentSizes[dc] -= k->docPartSize; + sentPartSize = k->docPartSize; docRequestsSent.erase(j); } + sentSize -= sentPartSize; + sentSizes[dc] -= sentPartSize; if (k->type() == PreparePhoto) { + k->fileSentSize += sentPartSize; + PhotoData *photo = App::photo(k->id()); + if (photo->uploading() && k->file) { + photo->uploadingData->size = k->file->partssize; + photo->uploadingData->offset = k->fileSentSize; + } emit photoProgress(k.key()); } else if (k->type() == PrepareDocument) { DocumentData *doc = App::document(k->id()); - if (doc->status == FileUploading) { + if (doc->uploading()) { doc->uploadOffset = (k->docSentParts - docRequestsSent.size()) * k->docPartSize; if (doc->uploadOffset > doc->size) { doc->uploadOffset = doc->size; @@ -297,7 +314,7 @@ void FileUploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { emit documentProgress(k.key()); } else if (k->type() == PrepareAudio) { AudioData *audio = App::audio(k->id()); - if (audio->status == FileUploading) { + if (audio->uploading()) { audio->uploadOffset = (k->docSentParts - docRequestsSent.size()) * k->docPartSize; if (audio->uploadOffset > audio->size) { audio->uploadOffset = audio->size; diff --git a/Telegram/SourceFiles/fileuploader.h b/Telegram/SourceFiles/fileuploader.h index 26b542bdb..460f8e170 100644 --- a/Telegram/SourceFiles/fileuploader.h +++ b/Telegram/SourceFiles/fileuploader.h @@ -35,12 +35,14 @@ public: int32 fullSize(const FullMsgId &msgId) const; void cancel(const FullMsgId &msgId); + void pause(const FullMsgId &msgId); void confirm(const FullMsgId &msgId); void clear(); public slots: + void unpause(); void sendNext(); void killSessions(); @@ -101,6 +103,7 @@ private: FileLoadResultPtr file; ReadyLocalMedia media; int32 partsCount; + mutable int32 fileSentSize; uint64 id() const { return file ? file->id : media.id; @@ -136,7 +139,7 @@ private: uint32 sentSize; uint32 sentSizes[MTPUploadSessionsCount]; - FullMsgId uploading; + FullMsgId uploading, _paused; Queue queue; Queue uploaded; QTimer nextTimer, killSessionsTimer; diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 7a30eb6d5..b51d08b13 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -619,97 +619,165 @@ int64 imageCacheSize() { return globalAquiredSize; } -StorageImage::StorageImage(const StorageImageLocation &location, int32 size) : w(location.width), h(location.height), loader(new mtpFileLoader(location.dc, location.volume, location.local, location.secret, size)) { +StorageImage::StorageImage(const StorageImageLocation &location, int32 size) : _location(location), _size(size), _loader(0) { } -StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &bytes) : w(location.width), h(location.height), loader(0) { +StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &bytes) : _location(location), _size(bytes.size()), _loader(0) { setData(bytes); - if (location.dc) { - Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes)); + if (!_location.isNull()) { + Local::writeImage(storageKey(_location), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes)); } } const QPixmap &StorageImage::pixData() const { - return data; + return _data; } int32 StorageImage::width() const { - return w; + return _location.width(); } int32 StorageImage::height() const { - return h; + return _location.height(); } -bool StorageImage::check() const { - if (loader->done()) { - if (!data.isNull()) { - globalAquiredSize -= int64(data.width()) * data.height() * 4; - } - format = loader->imageFormat(); - data = loader->imagePixmap(); - QByteArray bytes = loader->bytes(); - if (!data.isNull()) { - globalAquiredSize += int64(data.width()) * data.height() * 4; - } +void StorageImage::checkload() const { + if (!amLoading() || !_loader->done()) return; - w = data.width(); - h = data.height(); - invalidateSizeCache(); - loader->deleteLater(); - loader->rpcInvalidate(); - loader = 0; - - saved = bytes; - forgot = false; - return true; - } - return false; -} - -void StorageImage::setData(QByteArray &bytes, const QByteArray &format) { - QBuffer buffer(&bytes); - - if (!data.isNull()) { - globalAquiredSize -= int64(data.width()) * data.height() * 4; - } - QByteArray fmt(format); - data = QPixmap::fromImage(App::readImage(bytes, &fmt, false), Qt::ColorOnly); - if (!data.isNull()) { - globalAquiredSize += int64(data.width()) * data.height() * 4; + QPixmap data = _loader->imagePixmap(); + if (data.isNull()) { + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = CancelledFileLoader; + return; } - w = data.width(); - h = data.height(); + if (!_data.isNull()) { + globalAquiredSize -= int64(_data.width()) * _data.height() * 4; + } + + format = _loader->imageFormat(); + _data = data; + saved = _loader->bytes(); + const_cast(this)->_size = saved.size(); + const_cast(this)->_location.setSize(_data.width(), _data.height()); + globalAquiredSize += int64(_data.width()) * _data.height() * 4; + invalidateSizeCache(); - if (loader) { - loader->deleteLater(); - loader->rpcInvalidate(); - loader = 0; - } - this->saved = bytes; - this->format = fmt; + + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = 0; + forgot = false; } -StorageImage::~StorageImage() { - if (!data.isNull()) { - globalAquiredSize -= int64(data.width()) * data.height() * 4; +void StorageImage::setData(QByteArray &bytes, const QByteArray &bytesFormat) { + QBuffer buffer(&bytes); + + if (!_data.isNull()) { + globalAquiredSize -= int64(_data.width()) * _data.height() * 4; } - if (loader) { - loader->deleteLater(); - loader->rpcInvalidate(); - loader = 0; + QByteArray fmt(bytesFormat); + _data = QPixmap::fromImage(App::readImage(bytes, &fmt, false), Qt::ColorOnly); + if (!_data.isNull()) { + globalAquiredSize += int64(_data.width()) * _data.height() * 4; + _location.setSize(_data.width(), _data.height()); + } + + invalidateSizeCache(); + if (amLoading()) { + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = 0; + } + saved = bytes; + format = fmt; + forgot = false; +} + +void StorageImage::automaticLoad(const HistoryItem *item) { + if (loaded()) return; + + if (_loader != CancelledFileLoader && item) { + bool loadFromCloud = false; + if (item->history()->peer->isUser()) { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); + } else { + loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); + } + + if (_loader) { + if (loadFromCloud) _loader->permitLoadFromCloud(); + } else if (!_location.isNull()) { + _loader = new mtpFileLoader(&_location, _size, loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true); + _loader->start(); + } + } +} + +void StorageImage::load(bool loadFirst, bool prior) { + if (loaded()) return; + + if (!_loader && !_location.isNull()) { + _loader = new mtpFileLoader(&_location, _size, LoadFromCloudOrLocal, false); + } + if (amLoading()) { + _loader->start(loadFirst, prior); + } +} + +void StorageImage::loadEvenCancelled(bool loadFirst, bool prior) { + if (_loader == CancelledFileLoader) _loader = 0; + return load(loadFirst, prior); +} + +StorageImage::~StorageImage() { + if (!_data.isNull()) { + globalAquiredSize -= int64(_data.width()) * _data.height() * 4; + } + if (amLoading()) { + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = 0; } } bool StorageImage::loaded() const { - if (!loader) return true; - return check(); + checkload(); + return (!_data.isNull() || !saved.isNull()); +} + +bool StorageImage::loading() const { + return amLoading(); +} + +bool StorageImage::displayLoading() const { + return amLoading() && (!_loader->loadingLocal() || !_loader->autoLoading()); +} + +void StorageImage::cancel() { + if (!amLoading()) return; + + mtpFileLoader *l = _loader; + _loader = CancelledFileLoader; + if (l) { + l->cancel(); + l->deleteLater(); + l->rpcInvalidate(); + } +} + +float64 StorageImage::progress() const { + return amLoading() ? _loader->currentProgress() : (loaded() ? 1 : 0); +} + +int32 StorageImage::loadOffset() const { + return amLoading() ? _loader->currentOffset() : 0; } StorageImage *getImage(const StorageImageLocation &location, int32 size) { - StorageKey key(storageKey(location.dc, location.volume, location.local)); + StorageKey key(storageKey(location)); StorageImages::const_iterator i = storageImages.constFind(key); if (i == storageImages.cend()) { i = storageImages.insert(key, new StorageImage(location, size)); @@ -718,7 +786,7 @@ StorageImage *getImage(const StorageImageLocation &location, int32 size) { } StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes) { - StorageKey key(storageKey(location.dc, location.volume, location.local)); + StorageKey key(storageKey(location)); StorageImages::const_iterator i = storageImages.constFind(key); if (i == storageImages.cend()) { QByteArray bytesArr(bytes); @@ -726,8 +794,8 @@ StorageImage *getImage(const StorageImageLocation &location, const QByteArray &b } else if (!i.value()->loaded()) { QByteArray bytesArr(bytes); i.value()->setData(bytesArr); - if (location.dc) { - Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes)); + if (!location.isNull()) { + Local::writeImage(key, StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes)); } } return i.value(); diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 83359821b..b928c69aa 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -25,43 +25,116 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org QImage imageBlur(QImage img); void imageRound(QImage &img); -struct StorageImageLocation { - StorageImageLocation() : width(0), height(0), dc(0), volume(0), local(0), secret(0) { +inline uint32 packInt(int32 a) { + return (a < 0) ? uint32(int64(a) + 0x100000000LL) : uint32(a); +} +inline int32 unpackInt(uint32 a) { + return (a > 0x7FFFFFFFU) ? int32(int64(a) - 0x100000000LL) : int32(a); +} +inline uint64 packUIntUInt(uint32 a, uint32 b) { + return (uint64(a) << 32) | uint64(b); +} +inline uint64 packUIntInt(uint32 a, int32 b) { + return packUIntUInt(a, packInt(b)); +} +inline uint64 packIntUInt(int32 a, uint32 b) { + return packUIntUInt(packInt(a), b); +} +inline uint64 packIntInt(int32 a, int32 b) { + return packUIntUInt(packInt(a), packInt(b)); +} +inline uint32 unpackUIntFirst(uint64 v) { + return uint32(v >> 32); +} +inline int32 unpackIntFirst(uint64 v) { + return unpackInt(unpackUIntFirst(v)); +} +inline uint32 unpackUIntSecond(uint64 v) { + return uint32(v & 0xFFFFFFFFULL); +} +inline int32 unpackIntSecond(uint64 v) { + return unpackInt(unpackUIntSecond(v)); +} + +class StorageImageLocation { +public: + StorageImageLocation() : _widthheight(0), _dclocal(0), _volume(0), _secret(0) { } - StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : width(width), height(height), dc(dc), volume(volume), local(local), secret(secret) { + StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : _widthheight(packIntInt(width, height)), _dclocal(packIntInt(dc, local)), _volume(volume), _secret(secret) { } - StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : width(width), height(height), dc(location.vdc_id.v), volume(location.vvolume_id.v), local(location.vlocal_id.v), secret(location.vsecret.v) { + StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : _widthheight(packIntInt(width, height)), _dclocal(packIntInt(location.vdc_id.v, location.vlocal_id.v)), _volume(location.vvolume_id.v), _secret(location.vsecret.v) { } bool isNull() const { - return !dc; + return !_dclocal; } - int32 width, height; - int32 dc; - uint64 volume; - int32 local; - uint64 secret; + int32 width() const { + return unpackIntFirst(_widthheight); + } + int32 height() const { + return unpackIntSecond(_widthheight); + } + void setSize(int32 width, int32 height) { + _widthheight = packIntInt(width, height); + } + int32 dc() const { + return unpackIntFirst(_dclocal); + } + uint64 volume() const { + return _volume; + } + int32 local() const { + return unpackIntSecond(_dclocal); + } + uint64 secret() const { + return _secret; + } + +private: + uint64 _widthheight; + uint64 _dclocal; + uint64 _volume; + uint64 _secret; + + friend inline bool operator==(const StorageImageLocation &a, const StorageImageLocation &b) { + return (a._dclocal == b._dclocal) && (a._volume == b._volume) && (a._secret == b._secret); + } + }; -inline bool operator==(const StorageImageLocation &a, const StorageImageLocation &b) { - return (a.width == b.width) && (a.height == b.height) && (a.dc == b.dc) && (a.volume == b.volume) && (a.local == b.local) && (a.secret == b.secret); -} inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation &b) { return !(a == b); } QPixmap imagePix(QImage img, int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh); +class HistoryItem; class Image { public: Image(QByteArray format = "PNG") : format(format), forgot(false) { } + + virtual void automaticLoad(const HistoryItem *item) { // auto load photo + } + virtual bool loaded() const { return true; } virtual bool loading() const { return false; } + virtual bool displayLoading() const { + return false; + } + virtual void cancel() { + } + virtual float64 progress() const { + return 1; + } + virtual int32 loadOffset() const { + return 0; + } + const QPixmap &pix(int32 w = 0, int32 h = 0) const; const QPixmap &pixRounded(int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const; @@ -76,10 +149,9 @@ public: virtual int32 width() const = 0; virtual int32 height() const = 0; - virtual void load(bool /*loadFirst*/ = false, bool /*prior*/ = true) { + virtual void load(bool loadFirst = false, bool prior = true) { } - - virtual void checkload() const { + virtual void loadEvenCancelled(bool loadFirst = false, bool prior = true) { } bool isNull() const; @@ -100,6 +172,9 @@ public: protected: + virtual void checkload() const { + } + virtual const QPixmap &pixData() const = 0; virtual void doForget() const = 0; virtual void doRestore() const = 0; @@ -165,7 +240,7 @@ inline StorageKey storageKey(const MTPDfileLocation &location) { return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v); } inline StorageKey storageKey(const StorageImageLocation &location) { - return storageKey(location.dc, location.volume, location.local); + return storageKey(location.dc(), location.volume(), location.local()); } class StorageImage : public Image { @@ -176,35 +251,29 @@ public: int32 width() const; int32 height() const; + + void automaticLoad(const HistoryItem *item); // auto load photo + bool loaded() const; - bool loading() const { - return loader ? loader->loading() : false; - } + bool loading() const; + bool displayLoading() const; + void cancel(); + float64 progress() const; + int32 loadOffset() const; + void setData(QByteArray &bytes, const QByteArray &format = QByteArray()); - void load(bool loadFirst = false, bool prior = true) { - if (loader) { - loader->start(loadFirst, prior); - if (loader) check(); - } - } - void checkload() const { - if (loader) { - if (!loader->loading()) { - loader->start(true); - } - if (loader) check(); - } - } + void load(bool loadFirst = false, bool prior = true); + void loadEvenCancelled(bool loadFirst = false, bool prior = true); ~StorageImage(); protected: const QPixmap &pixData() const; - bool check() const; + void checkload() const; void doForget() const { - data = QPixmap(); + _data = QPixmap(); } void doRestore() const { QBuffer buffer(&saved); @@ -212,14 +281,19 @@ protected: #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) reader.setAutoTransform(true); #endif - data = QPixmap::fromImageReader(&reader, Qt::ColorOnly); + _data = QPixmap::fromImageReader(&reader, Qt::ColorOnly); } private: + mutable QPixmap _data; + StorageImageLocation _location; + int32 _size; + mutable mtpFileLoader *_loader; + + bool amLoading() const { + return _loader && _loader != CancelledFileLoader; + } - mutable QPixmap data; - mutable int32 w, h; - mutable mtpFileLoader *loader; }; StorageImage *getImage(const StorageImageLocation &location, int32 size = 0); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c3c5ca16d..31097c94d 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2878,44 +2878,6 @@ void HistoryBlock::removeItem(HistoryItem *item) { } } -void ItemAnimations::step_animate(float64 ms, bool timer) { - for (Animations::iterator i = _animations.begin(); i != _animations.end();) { - const HistoryItem *item = i.key(); - if (item->animating()) { - if (timer) Ui::redrawHistoryItem(item); - ++i; - } else { - i = _animations.erase(i); - } - } - if (_animations.isEmpty()) { - _a_animate.stop(); - } -} - -uint64 ItemAnimations::animate(const HistoryItem *item, uint64 ms) { - if (_animations.isEmpty()) { - _animations.insert(item, ms); - _a_animate.start(); - return 0; - } - Animations::const_iterator i = _animations.constFind(item); - if (i == _animations.cend()) i = _animations.insert(item, ms); - return ms - i.value(); -} - -void ItemAnimations::remove(const HistoryItem *item) { - _animations.remove(item); -} - -namespace { - ItemAnimations _itemAnimations; -} - -ItemAnimations &itemAnimations() { - return _itemAnimations; -} - HistoryItem::HistoryItem(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime msgDate, int32 from) : y(0) , id(msgId) , date(msgDate) @@ -2989,7 +2951,6 @@ void HistoryItem::setId(MsgId newId) { } HistoryItem::~HistoryItem() { - itemAnimations().remove(this); App::historyUnregItem(this); if (id < 0) { App::app()->uploader()->cancel(fullId()); @@ -3079,35 +3040,49 @@ void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, cons p.setOpacity(o); } -HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent) : HistoryMedia() +HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent) : HistoryFileMedia() , _data(App::feedPhoto(photo)) -, _openl(new PhotoLink(_data)) , _pixw(1) , _pixh(1) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { + setLinks(new PhotoLink(_data), new PhotoLink(_data), new PhotoCancelLink(_data)); + if (!caption.isEmpty()) { _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); } init(); } -HistoryPhoto::HistoryPhoto(PhotoData *photo) : HistoryMedia() +HistoryPhoto::HistoryPhoto(PhotoData *photo) : HistoryFileMedia() , _data(photo) -, _openl(new PhotoLink(_data)) , _pixw(1) , _pixh(1) { + setLinks(new PhotoLink(_data), new PhotoLink(_data), new PhotoCancelLink(_data)); + init(); } -HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryMedia() +HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryFileMedia() , _data(App::feedPhoto(photo)) -, _openl(new PhotoLink(_data, chat)) , _pixw(1) , _pixh(1) { + setLinks(new PhotoLink(_data, chat), new PhotoLink(_data, chat), new PhotoCancelLink(_data)); + _width = width; init(); } +HistoryPhoto::HistoryPhoto(const HistoryPhoto &other) : HistoryFileMedia() +, _data(other._data) +, _caption(other._caption) +, _pixw(other._pixw) +, _pixh(other._pixh) { + setLinks(new PhotoLink(_data), new PhotoLink(_data), new PhotoCancelLink(_data)); + + init(); +} + + void HistoryPhoto::init() { _data->thumb->load(); } @@ -3196,12 +3171,25 @@ int32 HistoryPhoto::resize(int32 width, const HistoryItem *parent) { void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; + + _data->automaticLoad(parent); + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); + + bool notChild = (parent->getMedia() == this); int32 skipx = 0, skipy = 0, width = _width, height = _height; bool bubble = parent->hasBubble(); bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); + if (displayLoading) { + ensureAnimation(parent); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); + } + } + bool radial = isRadialAnimation(ms); + if (bubble) { skipx = st::mediaPadding.left(); skipy = st::mediaPadding.top(); @@ -3214,45 +3202,61 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } else { App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } - _data->full->load(false, false); - bool full = _data->full->loaded(); QPixmap pix; - if (full) { + if (loaded) { pix = _data->full->pixSingle(_pixw, _pixh, width, height); } else { pix = _data->thumb->pixBlurredSingle(_pixw, _pixh, width, height); } - - p.drawPixmapLeft(skipx, skipy, _width, pix); - if (!full) { - uint64 dt = itemAnimations().animate(parent, ms); - int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); - - int32 x = (width - st::photoLoader.width()) / 2, y = (height - st::photoLoader.height()) / 2; - p.fillRect(skipx + x, skipy + y, st::photoLoader.width(), st::photoLoader.height(), st::photoLoaderBg->b); - x += (st::photoLoader.width() - cnt * st::photoLoaderPoint.width() - (cnt - 1) * st::photoLoaderSkip) / 2; - y += (st::photoLoader.height() - st::photoLoaderPoint.height()) / 2; - QColor c(st::white->c); - QBrush b(c); - for (int32 i = 0; i < cnt; ++i) { - t -= delta; - while (t < 0) t += period; - - float64 alpha = (t >= st::photoLoaderDuration1 + st::photoLoaderDuration2) ? 0 : ((t > st::photoLoaderDuration1 ? ((st::photoLoaderDuration1 + st::photoLoaderDuration2 - t) / st::photoLoaderDuration2) : (t / st::photoLoaderDuration1))); - c.setAlphaF(st::photoLoaderAlphaMin + alpha * (1 - st::photoLoaderAlphaMin)); - b.setColor(c); - p.fillRect(skipx + x + i * (st::photoLoaderPoint.width() + st::photoLoaderSkip), skipy + y, st::photoLoaderPoint.width(), st::photoLoaderPoint.height(), b); - } + QRect rthumb(rtlrect(skipx, skipy, width, height, _width)); + p.drawPixmap(rthumb.topLeft(), pix); + if (selected) { + App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - if (selected) { - App::roundRect(p, rtlrect(skipx, skipy, width, height, _width), textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + if (notChild && (radial || (!loaded && !_data->loading()))) { + float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _animation->radial.opacity() : 1; + QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + p.setPen(Qt::NoPen); + if (selected) { + p.setBrush(st::msgDateImgBgSelected); + } else if (_animation && _animation->_a_thumbOver.animating()) { + _animation->_a_thumbOver.step(ms); + float64 over = _animation->a_thumbOver.current(); + p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); + p.setBrush(st::black); + } else { + bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); + p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); + } + + p.setOpacity(radialOpacity * p.opacity()); + + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + p.setOpacity(radial ? _animation->radial.opacity() : 1); + + p.setOpacity(radialOpacity); + style::sprite icon; + if (radial || _data->loading()) { + icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } else { + icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); + } + p.drawSpriteCenter(inner, icon); + if (radial) { + p.setOpacity(1); + QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); + _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg); + } } // date if (_caption.isEmpty()) { - if (parent->getMedia() == this) { + if (notChild) { int32 fullRight = skipx + width, fullBottom = skipy + height; parent->drawInfo(p, fullRight, fullBottom, 2 * skipx + width, selected, InfoDisplayOverImage); } @@ -3284,7 +3288,11 @@ void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x height -= skipy + st::mediaPadding.bottom(); } if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { - lnk = _openl; + if (_data->uploading()) { + lnk = _cancell; + } else { + lnk = _data->loaded() ? _openl : (_data->loading() ? _cancell : _savel); + } if (_caption.isEmpty() && parent->getMedia() == this) { int32 fullRight = skipx + width, fullBottom = skipy + height; bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); @@ -3299,21 +3307,25 @@ void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaPhoto) { const MTPPhoto &photo(media.c_messageMediaPhoto().vphoto); + App::feedPhoto(photo, _data); + if (photo.type() == mtpc_photo) { const QVector &sizes(photo.c_photo().vsizes.c_vector().v); - for (QVector::const_iterator i = sizes.cbegin(), e = sizes.cend(); i != e; ++i) { + int32 max = 0; + const MTPDfileLocation *maxLocation = 0; + for (int32 i = 0, l = sizes.size(); i < l; ++i) { char size = 0; const MTPFileLocation *loc = 0; - switch (i->type()) { + switch (sizes.at(i).type()) { case mtpc_photoSize: { - const string &s(i->c_photoSize().vtype.c_string().v); - loc = &i->c_photoSize().vlocation; + const string &s(sizes.at(i).c_photoSize().vtype.c_string().v); + loc = &sizes.at(i).c_photoSize().vlocation; if (s.size()) size = s[0]; } break; case mtpc_photoCachedSize: { - const string &s(i->c_photoCachedSize().vtype.c_string().v); - loc = &i->c_photoCachedSize().vlocation; + const string &s(sizes.at(i).c_photoCachedSize().vtype.c_string().v); + loc = &sizes.at(i).c_photoCachedSize().vlocation; if (s.size()) size = s[0]; } break; } @@ -3322,10 +3334,20 @@ void HistoryPhoto::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, Local::writeImage(storageKey(loc->c_fileLocation()), _data->thumb); } else if (size == 'm') { Local::writeImage(storageKey(loc->c_fileLocation()), _data->medium); - } else if (size == 'x') { - Local::writeImage(storageKey(loc->c_fileLocation()), _data->full); + } else if (size == 'x' && max < 1) { + max = 1; + maxLocation = &loc->c_fileLocation(); + } else if (size == 'y' && max < 2) { + max = 2; + maxLocation = &loc->c_fileLocation(); + //} else if (size == 'w' && max < 3) { + // max = 3; + // maxLocation = &loc->c_fileLocation(); } } + if (maxLocation) { + Local::writeImage(storageKey(*maxLocation), _data->full); + } } } } @@ -3508,7 +3530,7 @@ void HistoryVideo::initDimensions(const HistoryItem *parent) { _thumbw = qMax(tw, 1); int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); - _maxw = qMax(_thumbw, minWidth); + _maxw = qMax(_thumbw, int16(minWidth)); _minh = qMax(th, int32(st::minPhotoSize)); if (bubble) { _maxw += st::mediaPadding.left() + st::mediaPadding.right(); @@ -3544,7 +3566,7 @@ int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { int32 minWidth = qMax(st::minPhotoSize, parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x())); minWidth = qMax(minWidth, videoMaxStatusWidth(_data) + 2 * int32(st::msgDateImgDelta + st::msgDateImgPadding.x())); - _width = qMax(_thumbw, minWidth); + _width = qMax(_thumbw, int16(minWidth)); _height = qMax(th, int32(st::minPhotoSize)); if (bubble) { _width += st::mediaPadding.left() + st::mediaPadding.right(); @@ -3559,13 +3581,17 @@ int32 HistoryVideo::resize(int32 width, const HistoryItem *parent) { void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; + + _data->automaticLoad(parent); + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); + int32 skipx = 0, skipy = 0, width = _width, height = _height; bool bubble = parent->hasBubble(); bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; int32 captionw = width - st::msgPadding.left() - st::msgPadding.right(); - if (_data->loader) { + if (displayLoading) { ensureAnimation(parent); if (!_animation->radial.animating()) { _animation->radial.start(_data->progress()); @@ -3586,7 +3612,6 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } else { App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } - _data->thumb->checkload(); QRect rthumb(rtlrect(skipx, skipy, width, height, _width)); @@ -3606,7 +3631,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loader ? _cancell : _savel); + bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -3619,9 +3644,9 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } style::sprite icon; - if (!_data->already().isEmpty()) { + if (loaded) { icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); - } else if (_data->loader) { + } else if (radial || _data->loading()) { icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); @@ -3654,6 +3679,9 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; + + bool loaded = _data->loaded(); + int32 skipx = 0, skipy = 0, width = _width, height = _height; bool bubble = parent->hasBubble(); @@ -3674,7 +3702,7 @@ void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x height -= skipy + st::mediaPadding.bottom(); } if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { - lnk = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + lnk = loaded ? _openl : (_data->loading() ? _cancell : _savel); if (_caption.isEmpty() && parent->getMedia() == this) { int32 fullRight = skipx + width, fullBottom = skipy + height; bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); @@ -3705,8 +3733,8 @@ void HistoryVideo::updateStatusText(const HistoryItem *parent) const { statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { statusSize = _data->uploadOffset; - } else if (_data->loader) { - statusSize = _data->loader->currentOffset(); + } else if (_data->loading()) { + statusSize = _data->progress(); } else if (!_data->already().isEmpty()) { statusSize = FileStatusSizeLoaded; } else { @@ -3754,8 +3782,6 @@ HistoryAudio::HistoryAudio(const HistoryAudio &other) : HistoryFileMedia() } void HistoryAudio::initDimensions(const HistoryItem *parent) { - _data->prepareAutoLoader(parent); - _maxw = st::msgFileMinWidth; int32 tleft = 0, tright = 0; @@ -3773,14 +3799,12 @@ void HistoryAudio::initDimensions(const HistoryItem *parent) { void HistoryAudio::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - bool loaded = _data->loaded(); - if (!loaded && _data->status == FileReady && _data->loader && !_data->loadingStarted()) { - _data->openOnSave = 0; - _data->save(QString()); - } + _data->automaticLoad(parent); + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); - if (_data->loadingStarted() && !_data->loader->loadingLocal()) { + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + + if (displayLoading) { ensureAnimation(parent); if (!_animation->radial.animating()) { _animation->radial.start(_data->progress()); @@ -3805,7 +3829,7 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, const QRect &r, b float64 over = _animation->a_thumbOver.current(); p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); } else { - bool over = textlnkDrawOver(_data->loadingStarted() ? _cancell : _savel); + bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg)); } @@ -3822,10 +3846,10 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, const QRect &r, b style::sprite icon; if (showPause) { icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); - } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { - icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); - } else if (_data->loadingStarted()) { + } else if (radial || _data->loading()) { icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } else if (loaded) { + icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); } else { icon = outbg ? (selected ? st::msgFileOutDownloadSelected : st::msgFileOutDownload) : (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } @@ -3866,12 +3890,12 @@ void HistoryAudio::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width)); - if ((_data->loadingStarted() || _data->status == FileUploading || !loaded) && inner.contains(x, y)) { - lnk = (_data->loadingStarted() || _data->status == FileUploading) ? _cancell : _savel; + if ((_data->loading() || _data->status == FileUploading || !loaded) && inner.contains(x, y)) { + lnk = (_data->loading() || _data->status == FileUploading) ? _cancell : _savel; return; } - if (x >= 0 && y >= 0 && x < _width && y < _height && !_data->loadingStarted() && _data->access) { + if (x >= 0 && y >= 0 && x < _width && y < _height && _data->access && !_data->loading()) { lnk = _openl; return; } @@ -3896,8 +3920,8 @@ void HistoryAudio::unregItem(HistoryItem *item) { void HistoryAudio::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaAudio) { App::feedAudio(media.c_messageMediaAudio().vaudio, _data); - if (!_data->data.isEmpty()) { - Local::writeAudio(mediaKey(mtpToLocationType(mtpc_inputAudioFileLocation), _data->dc, _data->id), _data->data); + if (!_data->data().isEmpty()) { + Local::writeAudio(mediaKey(AudioFileLocation, _data->dc, _data->id), _data->data()); } } } @@ -3913,8 +3937,8 @@ bool HistoryAudio::updateStatusText(const HistoryItem *parent) const { statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { statusSize = _data->uploadOffset; - } else if (_data->loadingStarted()) { - statusSize = _data->loader->currentOffset(); + } else if (_data->loading()) { + statusSize = _data->loadOffset(); } else if (_data->loaded()) { AudioMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; @@ -3976,8 +4000,6 @@ HistoryDocument::HistoryDocument(const HistoryDocument &other) : HistoryFileMedi } void HistoryDocument::initDimensions(const HistoryItem *parent) { - _data->prepareAutoLoader(parent); - _maxw = st::msgFileMinWidth; int32 tleft = 0, tright = 0; @@ -4006,10 +4028,12 @@ void HistoryDocument::initDimensions(const HistoryItem *parent) { void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - bool loaded = _data->loaded(); + _data->automaticLoad(parent); + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); - if (_data->loadingStarted()) { + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + + if (displayLoading) { ensureAnimation(parent); if (!_animation->radial.animating()) { _animation->radial.start(_data->progress()); @@ -4038,35 +4062,35 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - if (radial || !loaded) { + if (radial || (!loaded && !_data->loading())) { + float64 radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1; QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); if (selected) { p.setBrush(st::msgDateImgBgSelected); - } else if (radial && loaded) { - p.setOpacity(st::msgDateImgBg->c.alphaF() * _animation->radial.opacity()); - p.setBrush(st::black); } else if (_animation && _animation->_a_thumbOver.animating()) { _animation->_a_thumbOver.step(ms); float64 over = _animation->a_thumbOver.current(); p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loadingStarted() ? _cancell : _savel); + bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } + p.setOpacity(radialOpacity * p.opacity()); p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(inner); p.setRenderHint(QPainter::HighQualityAntialiasing, false); + p.setOpacity(radialOpacity); style::sprite icon; - if (loaded || _data->loadingStarted()) { + if (radial || _data->loading()) { icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } - p.setOpacity(radial ? _animation->radial.opacity() : 1); + p.setOpacity((radial && loaded) ? _animation->radial.opacity() : 1); p.drawSpriteCenter(inner, icon); if (radial) { p.setOpacity(1); @@ -4077,7 +4101,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r } if (_data->status != FileUploadFailed) { - const TextLinkPtr &lnk((_data->loadingStarted() || _data->status == FileUploading) ? _linkcancell : _linksavel); + const TextLinkPtr &lnk((_data->loading() || _data->status == FileUploading) ? _linkcancell : _linksavel); bool over = textlnkDrawOver(lnk); p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont); p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg)); @@ -4097,7 +4121,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r float64 over = _animation->a_thumbOver.current(); p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); } else { - bool over = textlnkDrawOver(_data->loadingStarted() ? _cancell : _savel); + bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg)); } @@ -4114,7 +4138,9 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r style::sprite icon; if (showPause) { icon = outbg ? (selected ? st::msgFileOutPauseSelected : st::msgFileOutPause) : (selected ? st::msgFileInPauseSelected : st::msgFileInPause); - } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { + } else if (radial || _data->loading()) { + icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); + } else if (loaded) { if (_data->song()) { icon = outbg ? (selected ? st::msgFileOutPlaySelected : st::msgFileOutPlay) : (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); } else if (_data->isImage()) { @@ -4122,8 +4148,6 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r } else { icon = outbg ? (selected ? st::msgFileOutFileSelected : st::msgFileOutFile) : (selected ? st::msgFileInFileSelected : st::msgFileInFile); } - } else if (_data->loadingStarted()) { - icon = outbg ? (selected ? st::msgFileOutCancelSelected : st::msgFileOutCancel) : (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); } else { icon = outbg ? (selected ? st::msgFileOutDownloadSelected : st::msgFileOutDownload) : (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } @@ -4161,28 +4185,25 @@ void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int3 QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); - if (loaded) { - } else { - if (rthumb.contains(x, y)) { - lnk = (_data->loadingStarted() || _data->status == FileUploading) ? _cancell : _savel; - return; - } + if ((_data->loading() || _data->uploading() || !loaded) && rthumb.contains(x, y)) { + lnk = (_data->loading() || _data->uploading()) ? _cancell : _savel; + return; } if (_data->status != FileUploadFailed) { if (rtlrect(nameleft, linktop, _linkw, st::semiboldFont->height, _width).contains(x, y)) { - lnk = (_data->loadingStarted() || _data->status == FileUploading) ? _linkcancell : _linksavel; + lnk = (_data->loading() || _data->uploading()) ? _linkcancell : _linksavel; return; } } } else { QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width)); - if ((_data->loadingStarted() || _data->status == FileUploading || !loaded) && inner.contains(x, y)) { - lnk = (_data->loadingStarted() || _data->status == FileUploading) ? _cancell : _savel; + if ((_data->loading() || _data->uploading() || !loaded) && inner.contains(x, y)) { + lnk = (_data->loading() || _data->uploading()) ? _cancell : _savel; return; } } - if (x >= 0 && y >= 0 && x < _width && y < _height && !_data->loadingStarted() && _data->access) { + if (x >= 0 && y >= 0 && x < _width && y < _height && !_data->loading() && !_data->uploading() && _data->access) { lnk = _openl; return; } @@ -4220,8 +4241,8 @@ bool HistoryDocument::updateStatusText(const HistoryItem *parent) const { statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { statusSize = _data->uploadOffset; - } else if (_data->loadingStarted()) { - statusSize = _data->loader->currentOffset(); + } else if (_data->loading()) { + statusSize = _data->loadOffset(); } else if (_data->loaded()) { if (_data->song()) { SongMsgId playing; @@ -4277,7 +4298,7 @@ HistoryGif::HistoryGif(DocumentData *document) : HistoryFileMedia() , _thumbw(1) , _thumbh(1) , _gif(0) { - setLinks(new GifOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data)); + setLinks(new DocumentOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data)); setStatusSize(FileStatusSizeReady); @@ -4289,14 +4310,12 @@ HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() , _thumbw(other._thumbw) , _thumbh(other._thumbh) , _gif(0) { - setLinks(new GifOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data)); + setLinks(new DocumentOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data)); setStatusSize(other._statusSize); } void HistoryGif::initDimensions(const HistoryItem *parent) { - _data->prepareAutoLoader(parent); - bool bubble = parent->hasBubble(); int32 tw = 0, th = 0; if (_gif && _gif->state() == ClipError) { @@ -4397,23 +4416,21 @@ int32 HistoryGif::resize(int32 width, const HistoryItem *parent) { void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; - int32 skipx = 0, skipy = 0, width = _width, height = _height; - bool bubble = parent->hasBubble(); - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; - bool loaded = _data->loaded(_gif ? false : true); - if (!loaded && _data->status == FileReady && _data->loader && !_data->loadingStarted()) { - _data->openOnSave = 0; - _data->save(QString()); - } + _data->automaticLoad(parent); + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); if (loaded && !_gif) { const_cast(this)->playInline(const_cast(parent)); } + int32 skipx = 0, skipy = 0, width = _width, height = _height; + bool bubble = parent->hasBubble(); + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel; + bool animating = (_gif && _gif->started()); - if (!animating) { - if (_data->loadingStarted() && !_data->loader->loadingLocal()) { + if (!animating || _data->uploading()) { + if (displayLoading) { ensureAnimation(parent); if (!_animation->radial.animating()) { _animation->radial.start(_data->progress()); @@ -4432,7 +4449,6 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo } else { App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); } - _data->thumb->checkload(); QRect rthumb(rtlrect(skipx, skipy, width, height, _width)); @@ -4445,7 +4461,8 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - if (radial || (!animating && !_gif && (!_data->loader || !_data->loader->loadingLocal() || _data->size > AnimationInMemory))) { + if (radial || (!_gif && !loaded && !_data->loading())) { + float64 radialOpacity = radial ? _animation->radial.opacity() : 1; QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); if (selected) { @@ -4456,33 +4473,32 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loadingStarted() ? _cancell : _savel); + bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } + p.setOpacity(radialOpacity * p.opacity()); p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(inner); p.setRenderHint(QPainter::HighQualityAntialiasing, false); - if (!selected && _animation) { - p.setOpacity(1); - } - + p.setOpacity(radialOpacity); style::sprite icon; if (_data->loaded() && !radial) { icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); - } else if (_data->loadingStarted() || radial) { + } else if (radial || _data->loading()) { icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } p.drawSpriteCenter(inner, icon); if (radial) { + p.setOpacity(1); QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); _animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg); } - if (!animating) { + if (!animating || _data->uploading()) { int32 statusX = skipx + st::msgDateImgDelta + st::msgDateImgPadding.x(), statusY = skipy + st::msgDateImgDelta + st::msgDateImgPadding.y(); int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); int32 statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); @@ -4512,11 +4528,11 @@ void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, height -= skipy + st::mediaPadding.bottom(); } if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { - if (_gif && _gif->started()) { - } else { - lnk = _data->loaded() ? _savel : (_data->loadingStarted() ? _cancell : _savel); + if (_data->uploading()) { + lnk = _cancell; + } else if (!_gif) { + lnk = _data->loaded() ? _openl : (_data->loading() ? _cancell : _savel); } - if (parent->getMedia() == this) { int32 fullRight = skipx + width, fullBottom = skipy + height; bool inDate = parent->pointInTime(fullRight, fullBottom, x, y, InfoDisplayOverImage); @@ -4547,8 +4563,8 @@ void HistoryGif::updateStatusText(const HistoryItem *parent) const { statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { statusSize = _data->uploadOffset; - } else if (_data->loadingStarted()) { - statusSize = _data->loader->currentOffset(); + } else if (_data->loading()) { + statusSize = _data->loadOffset(); } else if (_data->loaded()) { statusSize = FileStatusSizeLoaded; } else { @@ -4581,7 +4597,7 @@ bool HistoryGif::playInline(HistoryItem *parent) { if (_gif) { stopInline(parent); } else { - _gif = new ClipReader(_data->location(), _data->data); + _gif = new ClipReader(_data->location(), _data->data()); App::regGifItem(_gif, parent); } return true; @@ -4620,8 +4636,6 @@ HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() } void HistorySticker::initDimensions(const HistoryItem *parent) { - _data->prepareAutoLoader(parent); - _pixw = _data->dimensions.width(); _pixh = _data->dimensions.height(); if (_pixw > st::maxStickerSize) { @@ -4645,12 +4659,10 @@ void HistorySticker::initDimensions(const HistoryItem *parent) { void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; - bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; + _data->checkSticker(); bool loaded = _data->loaded(); - if (!loaded && _data->status == FileReady && _data->loader && !_data->loader->started()) { - _data->openOnSave = 0; - _data->save(QString()); - } + + bool out = parent->out(), fromChannel = parent->fromChannel(), outbg = out && !fromChannel, hovered, pressed; int32 usew = _maxw, usex = 0; const HistoryReply *reply = toHistoryReply(parent); @@ -4663,13 +4675,6 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, } if (rtl()) usex = _width - usex - usew; - if (_data->sticker()->img->isNull() && loaded) { - if (_data->data.isEmpty()) { - _data->sticker()->img = ImagePtr(_data->already()); - } else { - _data->sticker()->img = ImagePtr(_data->data); - } - } if (selected) { if (_data->sticker()->img->isNull()) { p.drawPixmap(QPoint(usex + (usew - _pixw) / 2, (_minh - _pixh) / 2), _data->thumb->pixBlurredColored(st::msgStickerOverlay, _pixw, _pixh)); @@ -4750,8 +4755,8 @@ void HistorySticker::unregItem(HistoryItem *item) { void HistorySticker::updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize) { if (media.type() == mtpc_messageMediaDocument) { App::feedDocument(media.c_messageMediaDocument().vdocument, _data); - if (!_data->data.isEmpty()) { - Local::writeStickerImage(mediaKey(mtpToLocationType(mtpc_inputDocumentFileLocation), _data->dc, _data->id), _data->data); + if (!_data->data().isEmpty()) { + Local::writeStickerImage(mediaKey(DocumentFileLocation, _data->dc, _data->id), _data->data()); } } } @@ -5346,7 +5351,7 @@ void HistoryWebPage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top(); if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth(); _attach->getState(lnk, state, x - attachLeft, y - attachTop, parent); - if (lnk && _data->photo) { + if (lnk && !_data->doc && _data->photo) { if (_data->type == WebPageProfile || _data->type == WebPageVideo) { lnk = _openl; } else if (_data->type == WebPagePhoto || _data->siteName == qstr("Twitter") || _data->siteName == qstr("Facebook")) { @@ -6036,10 +6041,6 @@ void HistoryMessage::fromNameUpdated() const { } } -bool HistoryMessage::uploading() const { - return _media ? _media->uploading() : false; -} - QString HistoryMessage::selectedText(uint32 selection) const { if (_media && selection == FullSelection) { QString text = _text.original(0, 0xFFFF, Text::ExpandLinksAll), mediaText = _media->inHistoryText(); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index b2351845d..84b750a58 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -768,23 +768,6 @@ protected: }; -class ItemAnimations { -public: - - ItemAnimations() : _a_animate(animation(this, &ItemAnimations::step_animate)) { - } - void step_animate(float64 ms, bool timer); - uint64 animate(const HistoryItem *item, uint64 ms); - void remove(const HistoryItem *item); - -private: - typedef QMap Animations; - Animations _animations; - Animation _a_animate; -}; - -ItemAnimations &itemAnimations(); - class HistoryReply; // dynamic_cast optimize class HistoryMessage; // dynamic_cast optimize class HistoryForwarded; // dynamic_cast optimize @@ -1014,10 +997,6 @@ public: return textcmdSkipBlock(skipBlockWidth(), skipBlockHeight()); } - virtual bool animating() const { - return false; - } - virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize return 0; } @@ -1218,70 +1197,6 @@ protected: }; -class HistoryPhoto : public HistoryMedia { -public: - - HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent); - HistoryPhoto(PhotoData *photo); - HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0); - void init(); - HistoryMediaType type() const { - return MediaTypePhoto; - } - HistoryMedia *clone() const { - return new HistoryPhoto(*this); - } - - void initDimensions(const HistoryItem *parent); - int32 resize(int32 width, const HistoryItem *parent); - - void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const; - - const QString inDialogsText() const; - const QString inHistoryText() const; - - PhotoData *photo() const { - return _data; - } - - void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize); - - virtual bool animating() const { - if (_data->full->loaded()) return false; - return _data->full->loading() ? true : !_data->medium->loaded(); - } - - bool hasReplyPreview() const { - return !_data->thumb->isNull(); - } - ImagePtr replyPreview(); - - QString getCaption() const { - return _caption.original(); - } - bool needsBubble(const HistoryItem *parent) const { - return !_caption.isEmpty() || parent->toHistoryReply(); - } - bool customInfoLayout() const { - return _caption.isEmpty(); - } - bool hideFromName() const { - return true; - } - bool hideForwardedFrom() const { - return true; - } - -private: - PhotoData *_data; - TextLinkPtr _openl; - - int16 _pixw, _pixh; - Text _caption; - -}; - class HistoryFileMedia : public HistoryMedia { public: @@ -1343,6 +1258,76 @@ private: }; +class HistoryPhoto : public HistoryFileMedia { +public: + + HistoryPhoto(const MTPDphoto &photo, const QString &caption, HistoryItem *parent); + HistoryPhoto(PhotoData *photo); + HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0); + HistoryPhoto(const HistoryPhoto &other); + void init(); + HistoryMediaType type() const { + return MediaTypePhoto; + } + HistoryMedia *clone() const { + return new HistoryPhoto(*this); + } + + void initDimensions(const HistoryItem *parent); + int32 resize(int32 width, const HistoryItem *parent); + + void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; + void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const; + + const QString inDialogsText() const; + const QString inHistoryText() const; + + PhotoData *photo() const { + return _data; + } + + void updateFrom(const MTPMessageMedia &media, HistoryItem *parent, bool allowEmitResize); + + bool hasReplyPreview() const { + return !_data->thumb->isNull(); + } + ImagePtr replyPreview(); + + QString getCaption() const { + return _caption.original(); + } + bool needsBubble(const HistoryItem *parent) const { + return !_caption.isEmpty() || parent->toHistoryReply(); + } + bool customInfoLayout() const { + return _caption.isEmpty(); + } + bool hideFromName() const { + return true; + } + bool hideForwardedFrom() const { + return true; + } + +protected: + + float64 dataProgress() const { + return _data->progress(); + } + bool dataFinished() const { + return !_data->loading() && !_data->uploading(); + } + bool dataLoaded() const { + return _data->loaded(); + } + +private: + PhotoData *_data; + int16 _pixw, _pixh; + Text _caption; + +}; + class HistoryVideo : public HistoryFileMedia { public: @@ -1369,7 +1354,7 @@ public: } bool uploading() const { - return (_data->status == FileUploading); + return _data->uploading(); } void regItem(HistoryItem *item); @@ -1399,16 +1384,16 @@ protected: return _data->progress(); } bool dataFinished() const { - return !_data->loader; + return !_data->loading() && !_data->uploading(); } bool dataLoaded() const { - return !_data->already().isEmpty(); + return _data->loaded(); } private: VideoData *_data; + int16 _thumbw; Text _caption; - int32 _thumbw; void setStatusSize(int32 newSize) const; void updateStatusText(const HistoryItem *parent) const; @@ -1436,7 +1421,7 @@ public: const QString inHistoryText() const; bool uploading() const { - return (_data->status == FileUploading); + return _data->uploading(); } AudioData *audio() { @@ -1464,7 +1449,7 @@ protected: return _data->progress(); } bool dataFinished() const { - return !_data->loader; + return !_data->loading() && !_data->uploading(); } bool dataLoaded() const { return _data->loaded(); @@ -1499,7 +1484,7 @@ public: const QString inHistoryText() const; bool uploading() const { - return (_data->status == FileUploading); + return _data->uploading(); } bool withThumb() const { @@ -1539,7 +1524,7 @@ protected: return _data->progress(); } bool dataFinished() const { - return !_data->loader; + return !_data->loading() && !_data->uploading(); } bool dataLoaded() const { return _data->loaded(); @@ -1584,7 +1569,7 @@ public: const QString inHistoryText() const; bool uploading() const { - return (_data->status == FileUploading); + return _data->uploading(); } DocumentData *getDocument() { @@ -1625,7 +1610,7 @@ protected: return _data->progress(); } bool dataFinished() const { - return !_data->loader; + return !_data->loading() && !_data->uploading(); } bool dataLoaded() const { return _data->loaded(); @@ -1807,10 +1792,6 @@ public: } ImagePtr replyPreview(); - virtual bool animating() const { - return _attach ? _attach->animating() : false; - } - WebPageData *webpage() { return _data; } @@ -1956,7 +1937,9 @@ public: bool displayFromName() const { return hasFromName() && (!emptyText() || !_media || !_media->isDisplayed() || toHistoryReply() || !_media->hideFromName()); } - bool uploading() const; + bool uploading() const { + return _media && _media->uploading(); + } void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const; void setViewsCount(int32 count); @@ -2034,9 +2017,6 @@ public: int32 viewsWidth() const { return _viewsWidth; } - virtual bool animating() const { - return _media ? _media->animating() : false; - } virtual QDateTime dateForwarded() const { // dynamic_cast optimize return date; @@ -2201,10 +2181,6 @@ public: HistoryMedia *getMedia(bool inOverview = false) const; - virtual bool animating() const { - return _media ? _media->animating() : false; - } - void setServiceText(const QString &text); ~HistoryServiceMsg(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 17ad05f62..ce290ee72 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -880,7 +880,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true); _menu->addAction(lang(lng_context_copy_image), this, SLOT(copyContextImage()))->setEnabled(true); } else { - if ((lnkVideo && lnkVideo->video()->loader) || (lnkAudio && lnkAudio->audio()->loadingStarted()) || (lnkDocument && lnkDocument->document()->loadingStarted())) { + if ((lnkVideo && lnkVideo->video()->loading()) || (lnkAudio && lnkAudio->audio()->loading()) || (lnkDocument && lnkDocument->document()->loading())) { _menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true); } else { if ((lnkVideo && !lnkVideo->video()->already(true).isEmpty()) || (lnkAudio && !lnkAudio->audio()->already(true).isEmpty()) || (lnkDocument && !lnkDocument->document()->already(true).isEmpty())) { @@ -1050,8 +1050,13 @@ void HistoryInner::cancelContextDownload() { VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data()); AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data()); DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data()); - mtpFileLoader *loader = lnkVideo ? lnkVideo->video()->loader : (lnkAudio ? lnkAudio->audio()->loader : (lnkDocument ? lnkDocument->document()->loader : 0)); - if (loader) loader->cancel(); + if (lnkVideo) { + lnkVideo->video()->cancel(); + } else if (lnkAudio) { + lnkAudio->audio()->cancel(); + } else if (lnkDocument) { + lnkDocument->document()->cancel(); + } } void HistoryInner::showContextInFolder() { @@ -5436,7 +5441,7 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, 0); } -// Ui::redrawHistoryItem(item); + Ui::redrawHistoryItem(item); } } diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 73baaabe1..e12b70e02 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -273,18 +273,7 @@ QSize StickerPreviewWidget::currentDimensions() const { QPixmap StickerPreviewWidget::currentImage() const { if (_doc && _cacheStatus != CacheLoaded) { - bool already = !_doc->already().isEmpty(), hasdata = !_doc->data.isEmpty(); - if (!already && !hasdata && !_doc->loader && _doc->status == FileReady) { - _doc->openOnSave = 0; - _doc->save(QString()); - } - if (_doc->sticker()->img->isNull() && (already || hasdata)) { - if (already) { - _doc->sticker()->img = ImagePtr(_doc->already()); - } else { - _doc->sticker()->img = ImagePtr(_doc->data); - } - } + _doc->checkSticker(); if (_doc->sticker()->img->isNull()) { if (_cacheStatus != CacheThumbLoaded) { QSize s = currentDimensions(); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index ac251ba18..e38b4f5ef 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -392,8 +392,10 @@ int32 LayoutOverviewVideo::resizeGetHeight(int32 width) { void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { bool selected = (selection == FullSelection), thumbLoaded = _data->thumb->loaded(); - bool already = !_data->already().isEmpty(); - if (_data->loader) { + + _data->automaticLoad(_parent); + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); + if (displayLoading) { ensureRadial(); if (!_radial->animating()) { _radial->start(_data->progress()); @@ -437,7 +439,7 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, p.fillRect(QRect(0, 0, _width, _height), st::overviewPhotoSelectOverlay); } - if (!selected && !context->selecting && !already) { + if (!selected && !context->selecting && !loaded) { if (clip.intersects(QRect(0, _height - st::normalFont->height, _width, st::normalFont->height))) { int32 statusX = st::msgDateImgPadding.x(), statusY = _height - st::normalFont->height - st::msgDateImgPadding.y(); int32 statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); @@ -470,7 +472,7 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(already ? _openl : (_data->loader ? _cancell : _savel)); + bool over = textlnkDrawOver(loaded ? _openl : (_data->loading() ? _cancell : _savel)); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -478,19 +480,18 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, p.drawEllipse(inner); p.setRenderHint(QPainter::HighQualityAntialiasing, false); + p.setOpacity(1); style::sprite icon; if (radial) { icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); - } else if (already) { + } else if (loaded) { icon = (selected ? st::msgFileInPlaySelected : st::msgFileInPlay); } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } - p.setOpacity(radial ? _radial->opacity() : 1); + p.setOpacity((radial && loaded) ? _radial->opacity() : 1); p.drawSpriteCenter(inner, icon); if (radial) { - p.setOpacity(1); - QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); _radial->draw(p, rinner, st::msgFileRadialLine, selected ? st::msgInBgSelected : st::msgInBg); } @@ -504,8 +505,10 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, } void LayoutOverviewVideo::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { + bool loaded = _data->loaded(); + if (hasPoint(x, y)) { - link = _data->already().isEmpty() ? (_data->loader ? _cancell : _savel) : _openl; + link = loaded ? _openl : (_data->loading() ? _cancell : _savel); } } @@ -516,8 +519,8 @@ void LayoutOverviewVideo::updateStatusText() const { statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { statusSize = _data->uploadOffset; - } else if (_data->loader) { - statusSize = _data->loader->currentOffset(); + } else if (_data->loading()) { + statusSize = _data->loadOffset(); } else if (!_data->already().isEmpty()) { statusSize = FileStatusSizeLoaded; } else { @@ -535,9 +538,8 @@ void LayoutOverviewVideo::updateStatusText() const { } LayoutOverviewAudio::LayoutOverviewAudio(AudioData *audio, HistoryItem *parent) : LayoutAbstractFileItem(parent) -, _data(audio) { - _data->prepareAutoLoader(_parent); - +, _data(audio) +, _namel(new AudioOpenLink(_data)) { setLinks(new AudioOpenLink(_data), new AudioOpenLink(_data), new AudioCancelLink(_data)); updateName(); QString d = textcmdLink(1, textRichPrepare(langDateTime(date(_data->date)))); @@ -553,13 +555,11 @@ void LayoutOverviewAudio::initDimensions() { void LayoutOverviewAudio::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { bool selected = (selection == FullSelection); - bool loaded = _data->loaded(); - if (!loaded && _data->status == FileReady && _data->loader && !_data->loadingStarted()) { - _data->openOnSave = 0; - _data->save(QString()); - } - if (_data->loadingStarted() && !_data->loader->loadingLocal()) { + _data->automaticLoad(_parent); + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); + + if (displayLoading) { ensureRadial(); if (!_radial->animating()) { _radial->start(_data->progress()); @@ -593,7 +593,7 @@ void LayoutOverviewAudio::paint(Painter &p, const QRect &clip, uint32 selection, float64 over = a_iconOver.current(); p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); } else { - bool over = textlnkDrawOver(loaded ? _openl : (_data->loadingStarted() ? _cancell : _savel)); + bool over = textlnkDrawOver(loaded ? _openl : (_data->loading() ? _cancell : _openl)); p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); } @@ -612,7 +612,7 @@ void LayoutOverviewAudio::paint(Painter &p, const QRect &clip, uint32 selection, icon = selected ? st::msgFileInPauseSelected : st::msgFileInPause; } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { icon = selected ? st::msgFileInPlaySelected : st::msgFileInPlay; - } else if (_data->loadingStarted()) { + } else if (_data->loading()) { icon = selected ? st::msgFileInCancelSelected : st::msgFileInCancel; } else { icon = selected ? st::msgFileInDownloadSelected : st::msgFileInDownload; @@ -654,7 +654,7 @@ void LayoutOverviewAudio::getState(TextLinkPtr &link, HistoryCursorState &cursor QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width)); if (inner.contains(x, y)) { - link = loaded ? _openl : ((_data->loadingStarted() || _data->status == FileUploading) ? _cancell : _savel); + link = loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _openl); return; } if (rtlrect(nameleft, statustop, _width - nameleft - nameright, st::normalFont->height, _width).contains(x, y)) { @@ -664,6 +664,10 @@ void LayoutOverviewAudio::getState(TextLinkPtr &link, HistoryCursorState &cursor cursor = inText ? HistoryInTextCursorState : HistoryDefaultCursorState; } } + if (hasPoint(x, y) && !link && !_data->loading()) { + link = _namel; + return; + } } void LayoutOverviewAudio::updateName() const { @@ -683,7 +687,7 @@ bool LayoutOverviewAudio::updateStatusText() const { int32 statusSize = 0, realDuration = 0; if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) { statusSize = FileStatusSizeFailed; - } else if (!_data->already().isEmpty() || !_data->data.isEmpty()) { + } else if (_data->loaded()) { AudioMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; int64 playingPosition = 0, playingDuration = 0; @@ -717,8 +721,6 @@ LayoutOverviewDocument::LayoutOverviewDocument(DocumentData *document, HistoryIt , _namew(st::semiboldFont->width(_name)) , _datew(st::normalFont->width(_date)) , _colorIndex(documentColorIndex(_data, _ext)) { - _data->prepareAutoLoader(_parent); - setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); setStatusSize(FileStatusSizeReady, _data->size, _data->song() ? _data->song()->duration : -1, 0); @@ -753,8 +755,11 @@ void LayoutOverviewDocument::initDimensions() { void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const { bool selected = (selection == FullSelection); - bool loaded = _data->loaded() || (_data->loader && _data->loader->localAvailable()); - if (_data->loadingStarted() && !_data->loader->loadingLocal()) { + + _data->automaticLoad(_parent); + bool loaded = _data->loaded() || Local::willStickerImageLoad(mediaKey(DocumentFileLocation, _data->dc, _data->id)), displayLoading = _data->displayLoading(); + + if (displayLoading) { ensureRadial(); if (!_radial->animating()) { _radial->start(_data->progress()); @@ -785,7 +790,7 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti float64 over = a_iconOver.current(); p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); } else { - bool over = textlnkDrawOver(loaded ? _openl : (_data->loadingStarted() ? _cancell : _savel)); + bool over = textlnkDrawOver(loaded ? _openl : (_data->loading() ? _cancell : _openl)); p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); } @@ -802,9 +807,9 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti style::sprite icon; if (showPause) { icon = selected ? st::msgFileInPauseSelected : st::msgFileInPause; - } else if (_statusSize < 0 || _statusSize == FileStatusSizeLoaded) { + } else if (loaded) { icon = selected ? st::msgFileInPlaySelected : st::msgFileInPlay; - } else if (_data->loadingStarted() && !_data->loader->loadingLocal()) { + } else if (_data->loading()) { icon = selected ? st::msgFileInCancelSelected : st::msgFileInCancel; } else { icon = selected ? st::msgFileInDownloadSelected : st::msgFileInDownload; @@ -847,36 +852,35 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - if (radial || (!loaded && !_data->loadingStarted())) { + if (radial || (!loaded && !_data->loading())) { QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); if (clip.intersects(inner)) { + float64 radialOpacity = (radial && loaded) ? _radial->opacity() : 1; p.setPen(Qt::NoPen); if (selected) { p.setBrush(st::msgDateImgBgSelected); - } else if (radial && loaded) { - p.setOpacity(st::msgDateImgBg->c.alphaF() * _radial->opacity()); - p.setBrush(st::black); } else if (_a_iconOver.animating()) { _a_iconOver.step(context->ms); float64 over = a_iconOver.current(); p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loadingStarted() ? _cancell : _savel); + bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } + p.setOpacity(radialOpacity * p.opacity()); p.setRenderHint(QPainter::HighQualityAntialiasing); p.drawEllipse(inner); p.setRenderHint(QPainter::HighQualityAntialiasing, false); + p.setOpacity(radialOpacity); style::sprite icon; - if (loaded || (_data->loadingStarted() && !_data->loader->loadingLocal())) { + if (loaded || _data->loading()) { icon = (selected ? st::msgFileInCancelSelected : st::msgFileInCancel); } else { icon = (selected ? st::msgFileInDownloadSelected : st::msgFileInDownload); } - p.setOpacity(radial ? _radial->opacity() : 1); p.drawSpriteCenter(inner, icon); if (radial) { p.setOpacity(1); @@ -919,7 +923,7 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti } void LayoutOverviewDocument::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { - bool loaded = _data->loaded() || (_data->loader && _data->loader->localAvailable()); + bool loaded = _data->loaded() || Local::willStickerImageLoad(mediaKey(DocumentFileLocation, _data->dc, _data->id)); bool showPause = updateStatusText(); @@ -934,7 +938,11 @@ void LayoutOverviewDocument::getState(TextLinkPtr &link, HistoryCursorState &cur QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, _width)); if (inner.contains(x, y)) { - link = loaded ? _openl : ((_data->loadingStarted() || _data->status == FileUploading) ? _cancell : _savel); + link = loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _openl); + return; + } + if (hasPoint(x, y) && !_data->loading()) { + link = _namel; return; } } else { @@ -946,7 +954,7 @@ void LayoutOverviewDocument::getState(TextLinkPtr &link, HistoryCursorState &cur QRect rthumb(rtlrect(0, st::linksBorder + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width)); if (rthumb.contains(x, y)) { - link = loaded ? _openl : ((_data->loadingStarted() || _data->status == FileUploading) ? _cancell : _savel); + link = loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _savel); return; } @@ -956,7 +964,7 @@ void LayoutOverviewDocument::getState(TextLinkPtr &link, HistoryCursorState &cur return; } } - if (!_data->loadingStarted() && _data->access) { + if (!_data->loading() && _data->access) { if (loaded && rtlrect(0, st::linksBorder, nameleft, _height - st::linksBorder, _width).contains(x, y)) { link = _namel; return; @@ -976,8 +984,8 @@ bool LayoutOverviewDocument::updateStatusText() const { statusSize = FileStatusSizeFailed; } else if (_data->status == FileUploading) { statusSize = _data->uploadOffset; - } else if (_data->loadingStarted()) { - statusSize = _data->loader->currentOffset(); + } else if (_data->loading()) { + statusSize = _data->loadOffset(); } else if (_data->loaded()) { if (_data->song()) { SongMsgId playing; diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index c2e79e66c..f606c6cea 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -323,10 +323,10 @@ protected: return _data->progress(); } virtual bool dataFinished() const { - return !_data->loader; + return !_data->loading(); } virtual bool dataLoaded() const { - return !_data->already().isEmpty(); + return _data->loaded(); } virtual bool iconAnimated() const { return true; @@ -363,10 +363,10 @@ protected: return _data->progress(); } virtual bool dataFinished() const { - return !_data->loader; + return !_data->loading(); } virtual bool dataLoaded() const { - return !_data->already().isEmpty() || !_data->data.isEmpty(); + return _data->loaded(); } virtual bool iconAnimated() const { return true; @@ -375,6 +375,7 @@ protected: private: OverviewItemInfo _info; AudioData *_data; + TextLinkPtr _namel; mutable Text _name, _details; mutable int32 _nameVersion; @@ -407,13 +408,13 @@ protected: return _data->progress(); } virtual bool dataFinished() const { - return !_data->loader; + return !_data->loading(); } virtual bool dataLoaded() const { return _data->loaded(); } virtual bool iconAnimated() const { - return _data->song() || !dataLoaded() || (_radial && _radial->animating()); + return _data->song() || !_data->loaded() || (_radial && _radial->animating()); } private: diff --git a/Telegram/SourceFiles/localimageloader.h b/Telegram/SourceFiles/localimageloader.h index 52a72a0a0..eace95819 100644 --- a/Telegram/SourceFiles/localimageloader.h +++ b/Telegram/SourceFiles/localimageloader.h @@ -194,6 +194,7 @@ struct FileLoadResult { int32 filesize; UploadFileParts fileparts; QByteArray filemd5; + int32 partssize; uint64 thumbId; // id is always file-id of media, thumbId is file-id of thumb ( == id for photos) QString thumbname; @@ -211,9 +212,11 @@ struct FileLoadResult { QString originalText; // when pasted had an image mime save text mime here to insert if image send was cancelled void setFileData(const QByteArray &filedata) { - if (!filedata.isEmpty()) { - int32 size = filedata.size(); - for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) { + if (filedata.isEmpty()) { + partssize = 0; + } else { + partssize = filedata.size(); + for (int32 i = 0, part = 0; i < partssize; i += UploadPartSize, ++part) { fileparts.insert(part, filedata.mid(i, UploadPartSize)); } filemd5.resize(32); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index c39db257b..589d24893 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -2469,13 +2469,6 @@ namespace Local { return _localLoader->addTask(new ImageLoadTask(j->first, location, loader)); } - bool willImageLoad(const StorageKey &location, bool check) { - StorageMap::const_iterator j = _imagesMap.constFind(location); - if (j == _imagesMap.cend()) return false; - if (check && !fileExists(j->first, UserPath)) return false; - return true; - } - int32 hasImages() { return _imagesMap.size(); } @@ -2535,11 +2528,8 @@ namespace Local { return _localLoader->addTask(new StickerImageLoadTask(j->first, location, loader)); } - bool willStickerImageLoad(const StorageKey &location, bool check) { - StorageMap::const_iterator j = _stickerImagesMap.constFind(location); - if (j == _stickerImagesMap.cend()) return false; - if (check && !fileExists(j->first, UserPath)) return false; - return true; + bool willStickerImageLoad(const StorageKey &location) { + return _stickerImagesMap.constFind(location) != _stickerImagesMap.cend(); } int32 hasStickers() { @@ -2601,13 +2591,6 @@ namespace Local { return _localLoader->addTask(new AudioLoadTask(j->first, location, loader)); } - bool willAudioLoad(const StorageKey &location, bool check) { - StorageMap::const_iterator j = _audiosMap.constFind(location); - if (j == _audiosMap.cend()) return false; - if (check && !fileExists(j->first, UserPath)) return false; - return true; - } - int32 hasAudios() { return _audiosMap.size(); } @@ -2623,8 +2606,8 @@ namespace Local { } void _writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc) { - stream << qint32(loc.width) << qint32(loc.height); - stream << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret); + stream << qint32(loc.width()) << qint32(loc.height()); + stream << qint32(loc.dc()) << quint64(loc.volume()) << qint32(loc.local()) << quint64(loc.secret()); } uint32 _storageImageLocationSize() { @@ -2896,7 +2879,7 @@ namespace Local { attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); } - DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.dc ? ImagePtr(thumb) : ImagePtr(), dc, size, thumb); + DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); if (!doc->sticker()) continue; set.stickers.push_back(doc); diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index 6d06f0659..eafc6066d 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -124,19 +124,17 @@ namespace Local { void writeImage(const StorageKey &location, const ImagePtr &img); void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true); TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader); - bool willImageLoad(const StorageKey &location, bool check = false); int32 hasImages(); qint64 storageImagesSize(); void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true); TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader); - bool willStickerImageLoad(const StorageKey &location, bool check = false); + bool willStickerImageLoad(const StorageKey &location); int32 hasStickers(); qint64 storageStickersSize(); void writeAudio(const StorageKey &location, const QByteArray &data, bool overwrite = true); TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader); - bool willAudioLoad(const StorageKey &location, bool check = false); int32 hasAudios(); qint64 storageAudiosSize(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index baca09cd1..bd90007b3 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "lang.h" #include "boxes/addcontactbox.h" +#include "fileuploader.h" #include "application.h" #include "window.h" #include "settingswidget.h" @@ -886,6 +887,12 @@ void MainWidget::deleteLayer(int32 selectedCount) { QString str((selectedCount < 0) ? lang(selectedCount < -1 ? lng_selected_cancel_sure_this : lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, selectedCount)); ConfirmBox *box = new ConfirmBox((selectedCount < 0) ? str : str.arg(selectedCount), lang(lng_box_delete)); if (selectedCount < 0) { + if (selectedCount < -1) { + if (HistoryItem *item = App::contextItem()) { + App::uploader()->pause(item->fullId()); + connect(box, SIGNAL(destroyed(QObject*)), App::uploader(), SLOT(unpause())); + } + } connect(box, SIGNAL(confirmed()), overview ? overview : static_cast(&history), SLOT(onDeleteContextSure())); } else { connect(box, SIGNAL(confirmed()), overview ? overview : static_cast(&history), SLOT(onDeleteSelectedSure())); @@ -1619,21 +1626,10 @@ void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMess void MainWidget::videoLoadProgress(mtpFileLoader *loader) { VideoData *video = App::video(loader->objId()); - if (video->loader) { - video->status = FileReady; - if (video->loader->done()) { - video->finish(); - QString already = video->already(); - if (!already.isEmpty() && video->openOnSave) { - QPoint pos(QCursor::pos()); - if (video->openOnSave < 0 && !psShowOpenWithMenu(pos.x(), pos.y(), already)) { - psOpenFile(already, true); - } else { - psOpenFile(already, video->openOnSave < 0); - } - } - } + if (video->loaded()) { + video->performActionOnLoad(); } + const VideoItems &items(App::videoItems()); VideoItems::const_iterator i = items.constFind(video); if (i != items.cend()) { @@ -1673,7 +1669,7 @@ void MainWidget::videoLoadFailed(mtpFileLoader *loader, bool started) { loadFailed(loader, started, SLOT(videoLoadRetry())); VideoData *video = App::video(loader->objId()); if (video) { - if (video->loader) video->finish(); + if (video->loading()) video->cancel(); video->status = FileDownloadFailed; } } @@ -1686,35 +1682,10 @@ void MainWidget::videoLoadRetry() { void MainWidget::audioLoadProgress(mtpFileLoader *loader) { AudioData *audio = App::audio(loader->objId()); - if (audio->loader) { - audio->status = FileReady; - if (audio->loader->done()) { - audio->finish(); - QString already = audio->already(); - bool play = audio->openOnSave > 0 && audio->openOnSaveMsgId.msg && audioPlayer(); - if ((!already.isEmpty() && audio->openOnSave) || (!audio->data.isEmpty() && play)) { - if (play) { - AudioMsgId playing; - AudioPlayerState state = AudioPlayerStopped; - audioPlayer()->currentState(&playing, &state); - if (playing.msgId == audio->openOnSaveMsgId && !(state & AudioPlayerStoppedMask) && state != AudioPlayerFinishing) { - audioPlayer()->pauseresume(OverviewAudios); - } else { - audioPlayer()->play(AudioMsgId(audio, audio->openOnSaveMsgId)); - if (App::main()) App::main()->audioMarkRead(audio); - } - } else { - QPoint pos(QCursor::pos()); - if (audio->openOnSave < 0 && !psShowOpenWithMenu(pos.x(), pos.y(), already)) { - psOpenFile(already, true); - } else { - psOpenFile(already, audio->openOnSave < 0); - } - if (App::main()) App::main()->audioMarkRead(audio); - } - } - } + if (audio->loaded()) { + audio->performActionOnLoad(); } + const AudioItems &items(App::audioItems()); AudioItems::const_iterator i = items.constFind(audio); if (i != items.cend()) { @@ -1733,13 +1704,13 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) { AudioData *audio = audioId.audio; QString already = audio->already(true); - if (already.isEmpty() && !audio->data.isEmpty()) { + if (already.isEmpty() && !audio->data().isEmpty()) { bool mp3 = (audio->mime == qstr("audio/mp3")); QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), mp3 ? qsl(".mp3") : qsl(".ogg"), false); if (!filename.isEmpty()) { QFile f(filename); if (f.open(QIODevice::WriteOnly)) { - if (f.write(audio->data) == audio->data.size()) { + if (f.write(audio->data()) == audio->data().size()) { f.close(); already = filename; audio->setLocation(FileLocation(StorageFilePartial, filename)); @@ -1770,7 +1741,7 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { DocumentData *document = songId.song; QString already = document->already(true); - if (already.isEmpty() && !document->data.isEmpty()) { + if (already.isEmpty() && !document->data().isEmpty()) { QString name = document->name, filter; MimeType mimeType = mimeTypeForName(document->mime); QStringList p = mimeType.globPatterns(); @@ -1787,7 +1758,7 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { if (!filename.isEmpty()) { QFile f(filename); if (f.open(QIODevice::WriteOnly)) { - if (f.write(document->data) == document->data.size()) { + if (f.write(document->data()) == document->data().size()) { f.close(); already = filename; document->setLocation(FileLocation(StorageFilePartial, filename)); @@ -1832,8 +1803,8 @@ void MainWidget::audioLoadFailed(mtpFileLoader *loader, bool started) { loadFailed(loader, started, SLOT(audioLoadRetry())); AudioData *audio = App::audio(loader->objId()); if (audio) { + if (audio->loading()) audio->cancel(); audio->status = FileDownloadFailed; - if (audio->loader) audio->finish(); } } @@ -1844,70 +1815,11 @@ void MainWidget::audioLoadRetry() { } void MainWidget::documentLoadProgress(mtpFileLoader *loader) { - bool songPlayActivated = false; DocumentData *document = App::document(loader->objId()); - if (document->loader) { - document->status = FileReady; - if (document->loader->done()) { - document->finish(); - QString already = document->already(); - - HistoryItem *item = (document->openOnSave && document->openOnSaveMsgId.msg) ? App::histItemById(document->openOnSaveMsgId) : 0; - bool playMusic = document->song() && audioPlayer() && document->openOnSave && item; - bool playAnimation = document->isAnimation() && document->openOnSave > 0 && item && item->getMedia(); - if (document->openOnSave && (!already.isEmpty() || (!document->data.isEmpty() && (playMusic || playAnimation)))) { - if (playMusic) { - SongMsgId playing; - AudioPlayerState playingState = AudioPlayerStopped; - audioPlayer()->currentState(&playing, &playingState); - if (playing.msgId == item->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { - audioPlayer()->pauseresume(OverviewDocuments); - } else { - SongMsgId song(document, item->fullId()); - audioPlayer()->play(song); - if (App::main()) App::main()->documentPlayProgress(song); - } - - songPlayActivated = true; - } else if (document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) { - if (!document->data.isEmpty() && playAnimation) { - if (document->openOnSave > 1) { - item->getMedia()->playInline(item); - } else { - App::wnd()->showDocument(document, item); - } - } else { - const FileLocation &location(document->location(true)); - if (location.accessEnable()) { - if (document->openOnSave > 1) { - if (playAnimation) { - item->getMedia()->playInline(item); - } else { - psOpenFile(already); - } - } else { - if (playAnimation || (item && QImageReader(location.name()).canRead())) { - App::wnd()->showDocument(document, item); - } else { - psOpenFile(already); - } - } - location.accessDisable(); - } else { - psOpenFile(already); - } - } - } else { - QPoint pos(QCursor::pos()); - if (document->openOnSave < 0 && !psShowOpenWithMenu(pos.x(), pos.y(), already)) { - psOpenFile(already, true); - } else { - psOpenFile(already, document->openOnSave < 0); - } - } - } - } + if (document->loaded()) { + document->performActionOnLoad(); } + const DocumentItems &items(App::documentItems()); DocumentItems::const_iterator i = items.constFind(document); if (i != items.cend()) { @@ -1917,18 +1829,14 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { } App::wnd()->documentUpdated(document); - if (!songPlayActivated && audioPlayer()) { + if (!document->loaded() && document->loading() && document->song() && audioPlayer()) { SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; int64 playingPosition = 0, playingDuration = 0; int32 playingFrequency = 0; audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency); if (playing.song == document && !_player.isHidden()) { - if (document->loader) { - _player.updateState(playing, playingState, playingPosition, playingDuration, playingFrequency); - } else { - audioPlayer()->play(playing); - } + _player.updateState(playing, playingState, playingPosition, playingDuration, playingFrequency); } } } @@ -1937,7 +1845,7 @@ void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) { loadFailed(loader, started, SLOT(documentLoadRetry())); DocumentData *document = App::document(loader->objId()); if (document) { - if (document->loader) document->finish(); + if (document->loading()) document->cancel(); document->status = FileDownloadFailed; } } @@ -2124,7 +2032,7 @@ void MainWidget::updateScrollColors() { void MainWidget::setChatBackground(const App::WallPaper &wp) { _background = new App::WallPaper(wp); - _background->full->load(); + _background->full->loadEvenCancelled(); checkChatBackground(); } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 960c99c8c..32742f277 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -88,11 +88,7 @@ MediaView::MediaView() : TWidget(App::wnd()) , _docThumbx(0) , _docThumby(0) , _docThumbw(0) -, _docRadialFirst(0) -, _docRadialStart(0) -, _docRadialLast(0) -, _docRadialOpacity(1) -, a_docRadialStart(0, 1) +, _docRadial(animation(this, &MediaView::step_radial)) , _docDownload(this, lang(lng_media_download), st::mvDocLink) , _docSaveAs(this, lang(lng_mediaview_save_as), st::mvDocLink) , _docCancel(this, lang(lng_cancel), st::mvDocLink) @@ -130,8 +126,6 @@ MediaView::MediaView() : TWidget(App::wnd()) _saveMsgText.setLink(1, TextLinkPtr(new SaveMsgLink(this))); _transparentBrush = QBrush(App::sprite().copy(st::mvTransparentBrush)); - _docRadialPen = QPen(st::white->p); - _docRadialPen.setWidth(st::radialLine); setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint); moveToScreen(); @@ -259,9 +253,9 @@ void MediaView::stopGif() { void MediaView::documentUpdated(DocumentData *doc) { if (_doc && _doc == doc && !fileShown()) { - if ((_doc->loader && _docCancel.isHidden()) || (!_doc->loader && !_docCancel.isHidden())) { + if ((_doc->loading() && _docCancel.isHidden()) || (!_doc->loading() && !_docCancel.isHidden())) { updateControls(); - } else if (_doc->loader) { + } else if (_doc->loading()) { updateDocSize(); update(_docRect); } @@ -278,8 +272,8 @@ void MediaView::changingMsgId(HistoryItem *row, MsgId newId) { void MediaView::updateDocSize() { if (!_doc || fileShown()) return; - if (_doc->loader) { - quint64 ready = _doc->loader->currentOffset(), total = _doc->size; + if (_doc->loading()) { + quint64 ready = _doc->loadOffset(), total = _doc->size; QString readyStr, totalStr, mb; if (total >= 1024 * 1024) { // more than 1 mb qint64 readyTenthMb = (ready * 10 / (1024 * 1024)), totalTenthMb = (total * 10 / (1024 * 1024)); @@ -310,14 +304,14 @@ void MediaView::updateDocSize() { void MediaView::updateControls() { if (_doc && !fileShown()) { - if (_doc->loader) { + if (_doc->loading()) { _docDownload.hide(); _docSaveAs.hide(); _docCancel.moveToLeft(_docRect.x() + 2 * st::mvDocPadding + st::mvDocIconSize, _docRect.y() + st::mvDocPadding + st::mvDocLinksTop); _docCancel.show(); - if (!_docRadialFirst) _docRadialFirst = _docRadialLast = _docRadialStart = getms(); - if (!_a_state.animating()) _a_state.start(); - _a_state.step(); + if (!_docRadial.animating()) { + _docRadial.start(_doc->progress()); + } } else { if (_doc->loaded(true)) { _docDownload.hide(); @@ -398,7 +392,7 @@ void MediaView::updateControls() { } void MediaView::updateDropdown() { - _btnSaveCancel->setVisible(_doc && _doc->loader); + _btnSaveCancel->setVisible(_doc && _doc->loading()); _btnToMessage->setVisible(_msgid > 0); _btnShowInFolder->setVisible(_doc && !_doc->already(true).isEmpty()); _btnSaveAs->setVisible(true); @@ -449,52 +443,36 @@ void MediaView::step_state(uint64 ms, bool timer) { update(toUpdate); if (dt < 1) result = true; } - if (_doc && _docRadialStart > 0) { - float64 prg = _doc->loader ? qMax(_doc->loader->currentProgress(), 0.0001) : (_doc->status == FileDownloadFailed ? 0 : (_doc->loaded() ? 1 : 0)); - if (prg != a_docRadial.to()) { - a_docRadial.start(prg); - _docRadialStart = _docRadialLast; - } - _docRadialLast = ms; - - float64 dt = float64(ms - _docRadialStart), fulldt = float64(ms - _docRadialFirst); - _docRadialOpacity = qMin(fulldt / st::radialDuration, 1.); - if (_doc->loader) { - a_docRadial.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear); - result = true; - } else if (dt >= st::radialDuration || (_doc->loaded() && _doc->size < MediaViewImageSizeLimit && _doc->isAnimation())) { - a_docRadial.update(1, anim::linear); - result = true; - _docRadialFirst = _docRadialLast = _docRadialStart = 0; - a_docRadial = anim::fvalue(0, 0); - if (_doc->loaded() && _doc->size < MediaViewImageSizeLimit) { - if (!_doc->data.isEmpty() && _doc->isAnimation()) { - displayDocument(_doc, App::histItemById(_msgmigrated ? 0 : _channel, _msgid)); - } else { - const FileLocation &location(_doc->location(true)); - if (location.accessEnable()) { - if (QImageReader(location.name()).canRead()) { - displayDocument(_doc, App::histItemById(_msgmigrated ? 0 : _channel, _msgid)); - } - location.accessDisable(); - } - } - } - } else { - float64 r = dt / st::radialDuration; - a_docRadial.update(r, anim::linear); - result = true; - _docRadialOpacity *= 1 - r; - } - float64 fromstart = fulldt / st::radialPeriod; - a_docRadialStart.update(fromstart - qFloor(fromstart), anim::linear); - update(_docIconRect); - } if (!result && _animations.isEmpty()) { _a_state.stop(); } } +void MediaView::step_radial(uint64 ms, bool timer) { + if (!_doc) { + _docRadial.stop(); + return; + } + _docRadial.update(_doc->progress(), !_doc->loading(), ms); + if (timer && _docRadial.animating()) { + update(_docIconRect); + } + if (_doc->loaded() && _doc->size < MediaViewImageSizeLimit && (!_docRadial.animating() || _doc->isAnimation())) { + if (!_doc->data().isEmpty() && _doc->isAnimation()) { + displayDocument(_doc, App::histItemById(_msgmigrated ? 0 : _channel, _msgid)); + } else { + const FileLocation &location(_doc->location(true)); + if (location.accessEnable()) { + if (_doc->isAnimation() || QImageReader(location.name()).canRead()) { + displayDocument(_doc, App::histItemById(_msgmigrated ? 0 : _channel, _msgid)); + } + location.accessDisable(); + } + } + + } +} + MediaView::~MediaView() { delete _gif; setBadPointer(_gif); @@ -556,7 +534,7 @@ void MediaView::onSaveAs() { QString file; if (_doc) { const FileLocation &location(_doc->location(true)); - if (!_doc->data.isEmpty() || location.accessEnable()) { + if (!_doc->data().isEmpty() || location.accessEnable()) { QFileInfo alreadyInfo(location.name()); QDir alreadyDir(alreadyInfo.dir()); QString name = alreadyInfo.fileName(), filter; @@ -577,16 +555,16 @@ void MediaView::onSaveAs() { file = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, true, alreadyDir); psShowOverAll(this); if (!file.isEmpty() && file != location.name()) { - if (_doc->data.isEmpty()) { + if (_doc->data().isEmpty()) { QFile(location.name()).copy(file); } else { QFile f(file); f.open(QIODevice::WriteOnly); - f.write(_doc->data); + f.write(_doc->data()); } } - if (_doc->data.isEmpty()) location.accessDisable(); + if (_doc->data().isEmpty()) location.accessDisable(); } else { if (!fileShown()) { DocumentSaveLink::doSave(_doc, true); @@ -615,10 +593,13 @@ void MediaView::onSaveAs() { } void MediaView::onDocClick() { - if (_doc->loader && _doc->loader->started()) { + if (_doc->loading()) { onSaveCancel(); } else { - DocumentOpenLink::doOpen(_doc); + DocumentOpenLink::doOpen(_doc, ActionOnLoadNone); + if (_doc->loading() && !_docRadial.animating()) { + _docRadial.start(_doc->progress()); + } } } @@ -695,8 +676,8 @@ void MediaView::onDownload() { } void MediaView::onSaveCancel() { - if (_doc && _doc->loader) { - _doc->loader->cancel(); + if (_doc && _doc->loading()) { + _doc->cancel(); } } @@ -935,7 +916,7 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { _from = _user; } updateControls(); - _photo->full->load(); + _photo->full->loadEvenCancelled(); if (isHidden()) { psUpdateOverlayed(this); show(); @@ -956,35 +937,39 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty _caption = Text(); if (_doc) { - if (_doc->isAnimation() && !_doc->loaded() && _doc->status == FileReady && _doc->loader && !_doc->loadingStarted()) { - _doc->openOnSave = 0; - _doc->save(QString()); - } - - const FileLocation &location(_doc->location(true)); - if (_doc->sticker() && !_doc->sticker()->img->isNull() && _doc->sticker()->img->loaded()) { - _current = _doc->sticker()->img->pix(); - } else if (!_doc->data.isEmpty() && _doc->isAnimation()) { - if (!_gif) { - if (_doc->dimensions.width() && _doc->dimensions.height()) { - _current = _doc->thumb->pixNoCache(_doc->dimensions.width(), _doc->dimensions.height(), true, true, false, _doc->dimensions.width(), _doc->dimensions.height()); - } - _gif = new ClipReader(location, _doc->data); + if (_doc->sticker()) { + _doc->checkSticker(); + if (!_doc->sticker()->img->isNull()) { + _current = _doc->sticker()->img->pix(); + } else { + _current = _doc->thumb->pixBlurred(_doc->dimensions.width(), _doc->dimensions.height()); } - } else if (location.accessEnable()) { - if (_doc->isAnimation()) { + } else { + _doc->automaticLoad(item); + + const FileLocation &location(_doc->location(true)); + if (!_doc->data().isEmpty() && _doc->isAnimation()) { if (!_gif) { if (_doc->dimensions.width() && _doc->dimensions.height()) { _current = _doc->thumb->pixNoCache(_doc->dimensions.width(), _doc->dimensions.height(), true, true, false, _doc->dimensions.width(), _doc->dimensions.height()); } - _gif = new ClipReader(location, _doc->data); + _gif = new ClipReader(location, _doc->data()); } - } else { - if (QImageReader(location.name()).canRead()) { - _current = QPixmap::fromImage(App::readImage(location.name(), 0, false), Qt::ColorOnly); + } else if (location.accessEnable()) { + if (_doc->isAnimation()) { + if (!_gif) { + if (_doc->dimensions.width() && _doc->dimensions.height()) { + _current = _doc->thumb->pixNoCache(_doc->dimensions.width(), _doc->dimensions.height(), true, true, false, _doc->dimensions.width(), _doc->dimensions.height()); + } + _gif = new ClipReader(location, _doc->data()); + } + } else { + if (QImageReader(location.name()).canRead()) { + _current = QPixmap::fromImage(App::readImage(location.name(), 0, false), Qt::ColorOnly); + } } + location.accessDisable(); } - location.accessDisable(); } } @@ -1030,10 +1015,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty _docNameWidth = st::mvDocNameFont->width(_docName); } - _docRadialFirst = _docRadialLast = _docRadialStart = 0; - - float64 prg = (_doc && _doc->loader) ? _doc->loader->currentProgress() : 0; - a_docRadial = anim::fvalue(prg, qMax(prg, 0.0001)); + _docRadial.stop(); // _docSize is updated in updateControls() _docRect = QRect((width() - st::mvDocSize.width()) / 2, (height() - st::mvDocSize.height()) / 2, st::mvDocSize.width(), st::mvDocSize.height()); @@ -1134,6 +1116,8 @@ void MediaView::paintEvent(QPaintEvent *e) { int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); _current = _photo->thumb->pixNoCache(w, h, true, true); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); + } else if (_current.isNull()) { + _current = _photo->thumb->pix(); } } p.setOpacity(1); @@ -1211,10 +1195,17 @@ void MediaView::paintEvent(QPaintEvent *e) { if (_docRect.intersects(r)) { p.fillRect(_docRect, st::mvDocBg->b); if (_docIconRect.intersects(r)) { + bool radial = false; + float64 radialOpacity = 0; + if (_docRadial.animating()) { + _docRadial.step(ms); + radial = _docRadial.animating(); + radialOpacity = _docRadial.opacity(); + } icon = true; if (!_doc || _doc->thumb->isNull()) { p.fillRect(_docIconRect, _docIconColor->b); - if ((!_doc || _doc->loaded()) && (!_docRadialStart || _docRadialOpacity < 1)) { + if ((!_doc || _doc->loaded()) && (!radial || radialOpacity < 1)) { p.drawSprite(_docIconRect.topLeft() + QPoint(rtl() ? 0 : (_docIconRect.width() - _docIcon.pxWidth()), 0), _docIcon); p.setPen(st::mvDocExtColor->p); p.setFont(st::mvDocExtFont->f); @@ -1228,33 +1219,27 @@ void MediaView::paintEvent(QPaintEvent *e) { } float64 o = overLevel(OverIcon); - if (_doc && _docRadialStart > 0) { - if (!_doc->loaded() && _docRadialOpacity < 1) { - p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity) * (1 - _docRadialOpacity)); + if (radial) { + if (!_doc->loaded() && radialOpacity < 1) { + p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity) * (1 - radialOpacity)); p.drawSpriteCenter(_docIconRect, st::radialDownload); } - p.setRenderHint(QPainter::HighQualityAntialiasing); - QRect inner(QPoint(_docIconRect.x() + ((_docIconRect.width() - st::radialSize.width()) / 2), _docIconRect.y() + ((_docIconRect.height() - st::radialSize.height()) / 2)), st::radialSize); p.setPen(Qt::NoPen); - p.setBrush(st::black->b); - p.setOpacity(_docRadialOpacity * st::radialBgOpacity); - p.drawEllipse(inner); + p.setBrush(st::black); + p.setOpacity(radialOpacity * st::radialBgOpacity); - p.setOpacity((o * 1. + (1 - o) * st::radialCancelOpacity) * _docRadialOpacity); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.drawEllipse(inner); + p.setRenderHint(QPainter::HighQualityAntialiasing, false); + + p.setOpacity((o * 1. + (1 - o) * st::radialCancelOpacity) * radialOpacity); p.drawSpriteCenter(_docIconRect, st::radialCancel); + p.setOpacity(1); QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine))); - - p.setOpacity(_docRadialOpacity); - p.setPen(_docRadialPen); - - int len = 16 + a_docRadial.current() * 5744; - p.drawArc(arc, 1440 - a_docRadialStart.current() * 5760 - len, len); - - p.setOpacity(1); - p.setRenderHint(QPainter::HighQualityAntialiasing, false); + _docRadial.draw(p, arc, st::radialLine, st::white); } else if (_doc && !_doc->loaded()) { p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity)); p.drawSpriteCenter(_docIconRect, st::radialDownload); @@ -1406,7 +1391,7 @@ void MediaView::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier))) { onCopy(); } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) { - if (_doc && !_doc->loader && !fileShown()) { + if (_doc && !_doc->loading() && !fileShown()) { onDocClick(); } } else if (e->key() == Qt::Key_Left) { @@ -1577,13 +1562,7 @@ void MediaView::preloadData(int32 delta) { case MediaTypePhoto: static_cast(media)->photo()->forget(); break; case MediaTypeDocument: case MediaTypeGif: - case MediaTypeSticker: - DocumentData *doc = media->getDocument(); - doc->forget(); - if (!doc->data.isEmpty() && doc->prepareAutoLoader(item)) { - doc->data.clear(); - } - break; + case MediaTypeSticker: media->getDocument()->forget(); break; } } } @@ -1605,15 +1584,12 @@ void MediaView::preloadData(int32 delta) { if (HistoryItem *item = App::histItemById(previewHistory->channelId(), previewHistory->overview[_overview][previewIndex])) { if (HistoryMedia *media = item->getMedia()) { switch (media->type()) { - case MediaTypePhoto: static_cast(media)->photo()->full->load(); break; + case MediaTypePhoto: static_cast(media)->photo()->full->loadEvenCancelled(); break; case MediaTypeDocument: case MediaTypeGif: { DocumentData *doc = media->getDocument(); doc->thumb->load(); - if (doc->isAnimation() && !doc->loaded() && doc->status == FileReady && doc->loader && !doc->loadingStarted()) { - doc->openOnSave = 0; - doc->save(QString()); - } + doc->automaticLoad(item); } break; case MediaTypeSticker: media->getDocument()->sticker()->img->load(); break; } @@ -1629,7 +1605,7 @@ void MediaView::preloadData(int32 delta) { } for (int32 i = from; i <= to; ++i) { if (i >= 0 && i < _user->photos.size() && i != _index) { - _user->photos[i]->full->load(); + _user->photos[i]->full->loadEvenCancelled(); } } int32 forgetIndex = _index - delta * 2; diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 5068d1bf3..498fd5427 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -65,8 +65,6 @@ public: void updateControls(); void updateDropdown(); - void step_state(uint64 ms, bool timer); - void showSaveMsgFile(); void close(); @@ -116,6 +114,9 @@ private: void updateHeader(); void snapXY(); + void step_state(uint64 ms, bool timer); + void step_radial(uint64 ms, bool timer); + QBrush _transparentBrush; PhotoData *_photo; @@ -154,10 +155,7 @@ private: int32 _docNameWidth, _docSizeWidth, _docExtWidth; QRect _docRect, _docIconRect; int32 _docThumbx, _docThumby, _docThumbw; - uint64 _docRadialFirst, _docRadialStart, _docRadialLast; - float64 _docRadialOpacity; - QPen _docRadialPen; - anim::fvalue a_docRadial, a_docRadialStart; + RadialAnimation _docRadial; LinkButton _docDownload, _docSaveAs, _docCancel; History *_migrated, *_history; // if conversation photos or files overview diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp index 32aac4dcb..a5e31bbf7 100644 --- a/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp +++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.cpp @@ -26,14 +26,14 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "localstorage.h" namespace { - int32 _priority = 1; + int32 GlobalPriority = 1; struct DataRequested { DataRequested() { memset(v, 0, sizeof(v)); } int64 v[MTPDownloadSessionsCount]; }; - QMap _dataRequested; + QMap DataRequestedMap; } struct mtpFileLoaderQueue { mtpFileLoaderQueue() : queries(0), start(0), end(0) { @@ -47,63 +47,64 @@ namespace { LoaderQueues queues; } -mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &volume, int32 local, const uint64 &secret, int32 size) +mtpFileLoader::mtpFileLoader(const StorageImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading) : prev(0) , next(0) , priority(0) , _paused(false) -, inQueue(false) -, complete(false) +, _autoLoading(autoLoading) +, _inQueue(false) +, _complete(false) , _localStatus(LocalNotTried) -, skippedBytes(0) -, nextRequestOffset(0) -, lastComplete(false) -, dc(dc) +, _skippedBytes(0) +, _nextRequestOffset(0) +, _lastComplete(false) +, _dc(location->dc()) , _locationType(UnknownFileLocation) -, volume(volume) -, local(local) -, secret(secret) -, id(0) -, access(0) -, fileIsOpen(false) -, size(size) -, type(mtpc_storage_fileUnknown) +, _location(location) +, _id(0) +, _access(0) +, _fileIsOpen(false) +, _toCache(LoadToCacheAsWell) +, _fromCloud(fromCloud) +, _size(size) +, _type(mtpc_storage_fileUnknown) , _localTaskId(0) { - LoaderQueues::iterator i = queues.find(dc); + LoaderQueues::iterator i = queues.find(_dc); if (i == queues.cend()) { - i = queues.insert(dc, mtpFileLoaderQueue()); + i = queues.insert(_dc, mtpFileLoaderQueue()); } queue = &i.value(); } -mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, LocationType type, const QString &to, int32 size, bool todata) +mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, LocationType type, const QString &to, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading) : prev(0) , next(0) , priority(0) , _paused(false) -, inQueue(false) -, complete(false) +, _autoLoading(autoLoading) +, _inQueue(false) +, _complete(false) , _localStatus(LocalNotTried) -, skippedBytes(0) -, nextRequestOffset(0) -, lastComplete(false) -, dc(dc) +, _skippedBytes(0) +, _nextRequestOffset(0) +, _lastComplete(false) +, _dc(dc) , _locationType(type) -, volume(0) -, local(0) -, secret(0) -, id(id) -, access(access) -, file(to) -, fname(to) -, fileIsOpen(false) -, duplicateInData(todata) -, size(size) -, type(mtpc_storage_fileUnknown) +, _location(0) +, _id(id) +, _access(access) +, _file(to) +, _fname(to) +, _fileIsOpen(false) +, _toCache(toCache) +, _fromCloud(fromCloud) +, _size(size) +, _type(mtpc_storage_fileUnknown) , _localTaskId(0) { - LoaderQueues::iterator i = queues.find(MTP::dld[0] + dc); + LoaderQueues::iterator i = queues.find(MTP::dld[0] + _dc); if (i == queues.cend()) { - i = queues.insert(MTP::dld[0] + dc, mtpFileLoaderQueue()); + i = queues.insert(MTP::dld[0] + _dc, mtpFileLoaderQueue()); } queue = &i.value(); } @@ -124,40 +125,45 @@ QPixmap mtpFileLoader::imagePixmap() const { void mtpFileLoader::readImage() const { QByteArray format; - switch (type) { + switch (_type) { case mtpc_storage_fileGif: format = "GIF"; break; case mtpc_storage_fileJpeg: format = "JPG"; break; case mtpc_storage_filePng: format = "PNG"; break; default: format = QByteArray(); break; } - _imagePixmap = QPixmap::fromImage(App::readImage(data, &format, false), Qt::ColorOnly); + _imagePixmap = QPixmap::fromImage(App::readImage(_data, &format, false), Qt::ColorOnly); if (!_imagePixmap.isNull()) { _imageFormat = format; } } float64 mtpFileLoader::currentProgress() const { - if (complete) return 1; + if (_complete) return 1; if (!fullSize()) return 0; return float64(currentOffset()) / fullSize(); } int32 mtpFileLoader::currentOffset(bool includeSkipped) const { - return (fileIsOpen ? file.size() : data.size()) - (includeSkipped ? 0 : skippedBytes); + return (_fileIsOpen ? _file.size() : _data.size()) - (includeSkipped ? 0 : _skippedBytes); } int32 mtpFileLoader::fullSize() const { - return size; + return _size; } -void mtpFileLoader::setFileName(const QString &fileName) { - if (duplicateInData && fname.isEmpty() && !fileName.isEmpty()) { - file.setFileName(fname = fileName); - } +bool mtpFileLoader::setFileName(const QString &fileName) { + if (_toCache != LoadToCacheAsWell || !_fname.isEmpty()) return fileName.isEmpty(); + _fname = fileName; + _file.setFileName(_fname); + return true; +} + +void mtpFileLoader::permitLoadFromCloud() { + _fromCloud = LoadFromCloudOrLocal; } uint64 mtpFileLoader::objId() const { - return id; + return _id; } void mtpFileLoader::loadNext() { @@ -171,51 +177,35 @@ void mtpFileLoader::loadNext() { } } -void mtpFileLoader::finishFail() { - bool started = currentOffset(true) > 0; - cancelRequests(); - type = mtpc_storage_fileUnknown; - complete = true; - if (fileIsOpen) { - file.close(); - fileIsOpen = false; - file.remove(); - } - data = QByteArray(); - emit failed(this, started); - file.setFileName(fname = QString()); - loadNext(); -} - bool mtpFileLoader::loadPart() { - if (complete || lastComplete || (!requests.isEmpty() && !size)) return false; - if (size && nextRequestOffset >= size) return false; + if (_complete || _lastComplete || (!_requests.isEmpty() && !_size)) return false; + if (_size && _nextRequestOffset >= _size) return false; int32 limit = DocumentDownloadPartSize; MTPInputFileLocation loc; - switch (_locationType) { - case UnknownFileLocation: - loc = MTP_inputFileLocation(MTP_long(volume), MTP_int(local), MTP_long(secret)); + if (_location) { + loc = MTP_inputFileLocation(MTP_long(_location->volume()), MTP_int(_location->local()), MTP_long(_location->secret())); limit = DownloadPartSize; - break; - case VideoFileLocation: - loc = MTP_inputVideoFileLocation(MTP_long(id), MTP_long(access)); - break; - case AudioFileLocation: - loc = MTP_inputAudioFileLocation(MTP_long(id), MTP_long(access)); - break; - case DocumentFileLocation: - loc = MTP_inputDocumentFileLocation(MTP_long(id), MTP_long(access)); - break; - default: - finishFail(); - return false; - break; + } else { + switch (_locationType) { + case VideoFileLocation: + loc = MTP_inputVideoFileLocation(MTP_long(_id), MTP_long(_access)); + break; + case AudioFileLocation: + loc = MTP_inputAudioFileLocation(MTP_long(_id), MTP_long(_access)); + break; + case DocumentFileLocation: + loc = MTP_inputDocumentFileLocation(MTP_long(_id), MTP_long(_access)); + break; + default: + cancel(true); + return false; + break; + } } - - int32 offset = nextRequestOffset, dcIndex = 0; - DataRequested &dr(_dataRequested[dc]); - if (size) { + int32 offset = _nextRequestOffset, dcIndex = 0; + DataRequested &dr(DataRequestedMap[_dc]); + if (_size) { for (int32 i = 1; i < MTPDownloadSessionsCount; ++i) { if (dr.v[i] < dr.v[dcIndex]) { dcIndex = i; @@ -223,104 +213,104 @@ bool mtpFileLoader::loadPart() { } } - App::app()->killDownloadSessionsStop(dc); + App::app()->killDownloadSessionsStop(_dc); - mtpRequestId reqId = MTP::send(MTPupload_GetFile(MTPupload_getFile(loc, MTP_int(offset), MTP_int(limit))), rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dld[dcIndex] + dc, 50); + mtpRequestId reqId = MTP::send(MTPupload_GetFile(MTPupload_getFile(loc, MTP_int(offset), MTP_int(limit))), rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dld[dcIndex] + _dc, 50); ++queue->queries; dr.v[dcIndex] += limit; - requests.insert(reqId, dcIndex); - nextRequestOffset += limit; + _requests.insert(reqId, dcIndex); + _nextRequestOffset += limit; return true; } void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req) { // uint64 ms = getms(); - Requests::iterator i = requests.find(req); - if (i == requests.cend()) return loadNext(); + Requests::iterator i = _requests.find(req); + if (i == _requests.cend()) return loadNext(); int32 limit = (_locationType == UnknownFileLocation) ? DownloadPartSize : DocumentDownloadPartSize; int32 dcIndex = i.value(); - _dataRequested[dc].v[dcIndex] -= limit; + DataRequestedMap[_dc].v[dcIndex] -= limit; --queue->queries; - requests.erase(i); + _requests.erase(i); const MTPDupload_file &d(result.c_upload_file()); const string &bytes(d.vbytes.c_string().v); if (bytes.size()) { - if (fileIsOpen) { - int64 fsize = file.size(); + if (_fileIsOpen) { + int64 fsize = _file.size(); if (offset < fsize) { - skippedBytes -= bytes.size(); + _skippedBytes -= bytes.size(); } else if (offset > fsize) { - skippedBytes += offset - fsize; + _skippedBytes += offset - fsize; } - file.seek(offset); - if (file.write(bytes.data(), bytes.size()) != qint64(bytes.size())) { - return finishFail(); + _file.seek(offset); + if (_file.write(bytes.data(), bytes.size()) != qint64(bytes.size())) { + return cancel(true); } } else { - data.reserve(offset + bytes.size()); - if (offset > data.size()) { - skippedBytes += offset - data.size(); - data.resize(offset); + _data.reserve(offset + bytes.size()); + if (offset > _data.size()) { + _skippedBytes += offset - _data.size(); + _data.resize(offset); } - if (offset == data.size()) { - data.append(bytes.data(), bytes.size()); + if (offset == _data.size()) { + _data.append(bytes.data(), bytes.size()); } else { - skippedBytes -= bytes.size(); - if (int64(offset + bytes.size()) > data.size()) { - data.resize(offset + bytes.size()); + _skippedBytes -= bytes.size(); + if (int64(offset + bytes.size()) > _data.size()) { + _data.resize(offset + bytes.size()); } - memcpy(data.data() + offset, bytes.data(), bytes.size()); + memcpy(_data.data() + offset, bytes.data(), bytes.size()); } } } if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset - lastComplete = true; + _lastComplete = true; } - if (requests.isEmpty() && (lastComplete || (size && nextRequestOffset >= size))) { - if (!fname.isEmpty() && duplicateInData) { - if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly); - if (!fileIsOpen) { - return finishFail(); + if (_requests.isEmpty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) { + if (!_fname.isEmpty() && (_toCache == LoadToCacheAsWell)) { + if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly); + if (!_fileIsOpen) { + return cancel(true); } - if (file.write(data) != qint64(data.size())) { - return finishFail(); + if (_file.write(_data) != qint64(_data.size())) { + return cancel(true); } } - type = d.vtype.type(); - complete = true; - if (fileIsOpen) { - file.close(); - fileIsOpen = false; - psPostprocessFile(QFileInfo(file).absoluteFilePath()); + _type = d.vtype.type(); + _complete = true; + if (_fileIsOpen) { + _file.close(); + _fileIsOpen = false; + psPostprocessFile(QFileInfo(_file).absoluteFilePath()); } removeFromQueue(); emit App::wnd()->imageLoaded(); if (!queue->queries) { - App::app()->killDownloadSessionsStart(dc); + App::app()->killDownloadSessionsStart(_dc); } if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { if (_locationType != UnknownFileLocation) { // audio, video, document - MediaKey mkey = mediaKey(_locationType, dc, id); - if (!fname.isEmpty()) { - Local::writeFileLocation(mkey, FileLocation(mtpToStorageType(type), fname)); + MediaKey mkey = mediaKey(_locationType, _dc, _id); + if (!_fname.isEmpty()) { + Local::writeFileLocation(mkey, FileLocation(mtpToStorageType(_type), _fname)); } - if (duplicateInData) { + if (_toCache == LoadToCacheAsWell) { if (_locationType == DocumentFileLocation) { - Local::writeStickerImage(mkey, data); + Local::writeStickerImage(mkey, _data); } else if (_locationType == AudioFileLocation) { - Local::writeAudio(mkey, data); + Local::writeAudio(mkey, _data); } } } else { - Local::writeImage(storageKey(dc, volume, local), StorageImageSaved(mtpToStorageType(type), data)); + Local::writeImage(storageKey(*_location), StorageImageSaved(mtpToStorageType(_type), _data)); } } } @@ -331,12 +321,12 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe bool mtpFileLoader::partFailed(const RPCError &error) { if (mtpIsFlood(error)) return false; - finishFail(); + cancel(true); return true; } void mtpFileLoader::removeFromQueue() { - if (!inQueue) return; + if (!_inQueue) return; if (next) { next->prev = prev; } @@ -350,7 +340,7 @@ void mtpFileLoader::removeFromQueue() { queue->start = next; } next = prev = 0; - inQueue = false; + _inQueue = false; } void mtpFileLoader::pause() { @@ -366,11 +356,11 @@ bool mtpFileLoader::tryLoadLocal() { return true; } - if (_locationType == UnknownFileLocation) { - _localTaskId = Local::startImageLoad(storageKey(dc, volume, local), this); + if (_location) { + _localTaskId = Local::startImageLoad(storageKey(*_location), this); } else { - if (duplicateInData) { - MediaKey mkey = mediaKey(_locationType, dc, id); + if (_toCache == LoadToCacheAsWell) { + MediaKey mkey = mediaKey(_locationType, _dc, _id); if (_locationType == DocumentFileLocation) { _localTaskId = Local::startStickerImageLoad(mkey, this); } else if (_locationType == AudioFileLocation) { @@ -379,37 +369,14 @@ bool mtpFileLoader::tryLoadLocal() { } } - if (data.isEmpty()) { - if (_localTaskId) { - _localStatus = LocalLoading; - return true; - } - _localStatus = LocalNotFound; - return false; + if (_localStatus != LocalNotTried) { + return _complete; + } else if (_localTaskId) { + _localStatus = LocalLoading; + return true; } - - _localStatus = LocalLoaded; - if (!fname.isEmpty() && duplicateInData) { - if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly); - if (!fileIsOpen) { - finishFail(); - return true; - } - if (file.write(data) != qint64(data.size())) { - finishFail(); - return true; - } - } - complete = true; - if (fileIsOpen) { - file.close(); - fileIsOpen = false; - psPostprocessFile(QFileInfo(file).absoluteFilePath()); - } - emit App::wnd()->imageLoaded(); - emit progress(this); - loadNext(); - return true; + _localStatus = LocalNotFound; + return false; } void mtpFileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &imageFormat, const QPixmap &imagePixmap) { @@ -419,113 +386,93 @@ void mtpFileLoader::localLoaded(const StorageImageSaved &result, const QByteArra start(true); return; } - data = result.data; - type = mtpFromStorageType(result.type); + _data = result.data; + _type = mtpFromStorageType(result.type); if (!imagePixmap.isNull()) { _imageFormat = imageFormat; _imagePixmap = imagePixmap; } _localStatus = LocalLoaded; - if (!fname.isEmpty() && duplicateInData) { - if (!fileIsOpen) fileIsOpen = file.open(QIODevice::WriteOnly); - if (!fileIsOpen) { - finishFail(); + if (!_fname.isEmpty() && _toCache == LoadToCacheAsWell) { + if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly); + if (!_fileIsOpen) { + cancel(true); return; } - if (file.write(data) != qint64(data.size())) { - finishFail(); + if (_file.write(_data) != qint64(_data.size())) { + cancel(true); return; } } - complete = true; - if (fileIsOpen) { - file.close(); - fileIsOpen = false; - psPostprocessFile(QFileInfo(file).absoluteFilePath()); + + _complete = true; + if (_fileIsOpen) { + _file.close(); + _fileIsOpen = false; + psPostprocessFile(QFileInfo(_file).absoluteFilePath()); } emit App::wnd()->imageLoaded(); emit progress(this); loadNext(); } -bool mtpFileLoader::localAvailable() const { - if (_localStatus == LocalLoading || _localStatus == LocalLoaded || _localStatus == LocalNeedsTry) { - return true; - } - if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { - return false; - } - - bool result = false; - if (_locationType == UnknownFileLocation) { - result = Local::willImageLoad(storageKey(dc, volume, local)); - } else { - if (duplicateInData) { - MediaKey mkey = mediaKey(_locationType, dc, id); - if (_locationType == DocumentFileLocation) { - result = Local::willStickerImageLoad(mkey); - } else if (_locationType == AudioFileLocation) { - result = Local::willAudioLoad(mkey); - } - } - } - - _localStatus = result ? LocalNeedsTry : LocalNotFound; - return result; -} - void mtpFileLoader::start(bool loadFirst, bool prior) { if (_paused) { _paused = false; } - if (complete || tryLoadLocal()) return; + if (_complete || tryLoadLocal()) return; - if (!fname.isEmpty() && !duplicateInData && !fileIsOpen) { - fileIsOpen = file.open(QIODevice::WriteOnly); - if (!fileIsOpen) { - return finishFail(); + if (_fromCloud == LoadFromLocalOnly) { + cancel(); + return; + } + + if (!_fname.isEmpty() && _toCache == LoadToFileOnly && !_fileIsOpen) { + _fileIsOpen = _file.open(QIODevice::WriteOnly); + if (!_fileIsOpen) { + return cancel(true); } } mtpFileLoader *before = 0, *after = 0; if (prior) { - if (inQueue && priority == _priority) { + if (_inQueue && priority == GlobalPriority) { if (loadFirst) { if (!prev) return startLoading(loadFirst, prior); before = queue->start; } else { - if (!next || next->priority < _priority) return startLoading(loadFirst, prior); + if (!next || next->priority < GlobalPriority) return startLoading(loadFirst, prior); after = next; - while (after->next && after->next->priority == _priority) { + while (after->next && after->next->priority == GlobalPriority) { after = after->next; } } } else { - priority = _priority; + priority = GlobalPriority; if (loadFirst) { - if (inQueue && !prev) return startLoading(loadFirst, prior); + if (_inQueue && !prev) return startLoading(loadFirst, prior); before = queue->start; } else { - if (inQueue) { - if (next && next->priority == _priority) { + if (_inQueue) { + if (next && next->priority == GlobalPriority) { after = next; - } else if (prev && prev->priority < _priority) { + } else if (prev && prev->priority < GlobalPriority) { before = prev; - while (before->prev && before->prev->priority < _priority) { + while (before->prev && before->prev->priority < GlobalPriority) { before = before->prev; } } else { return startLoading(loadFirst, prior); } } else { - if (queue->start && queue->start->priority == _priority) { + if (queue->start && queue->start->priority == GlobalPriority) { after = queue->start; } else { before = queue->start; } } if (after) { - while (after->next && after->next->priority == _priority) { + while (after->next && after->next->priority == GlobalPriority) { after = after->next; } } @@ -533,20 +480,20 @@ void mtpFileLoader::start(bool loadFirst, bool prior) { } } else { if (loadFirst) { - if (inQueue && (!prev || prev->priority == _priority)) return startLoading(loadFirst, prior); + if (_inQueue && (!prev || prev->priority == GlobalPriority)) return startLoading(loadFirst, prior); before = prev; - while (before->prev && before->prev->priority != _priority) { + while (before->prev && before->prev->priority != GlobalPriority) { before = before->prev; } } else { - if (inQueue && !next) return startLoading(loadFirst, prior); + if (_inQueue && !next) return startLoading(loadFirst, prior); after = queue->end; } } removeFromQueue(); - inQueue = true; + _inQueue = true; if (!queue->start) { queue->start = queue->end = this; } else if (before) { @@ -576,40 +523,50 @@ void mtpFileLoader::start(bool loadFirst, bool prior) { } void mtpFileLoader::cancel() { + cancel(false); +} + +void mtpFileLoader::cancel(bool fail) { + bool started = currentOffset(true) > 0; cancelRequests(); - type = mtpc_storage_fileUnknown; - complete = true; - if (fileIsOpen) { - file.close(); - fileIsOpen = false; - file.remove(); + _type = mtpc_storage_fileUnknown; + _complete = true; + if (_fileIsOpen) { + _file.close(); + _fileIsOpen = false; + _file.remove(); } - data = QByteArray(); - file.setFileName(QString()); - emit progress(this); + _data = QByteArray(); + if (fail) { + emit failed(this, started); + } else { + emit progress(this); + } + _fname = QString(); + _file.setFileName(_fname); loadNext(); } void mtpFileLoader::cancelRequests() { - if (requests.isEmpty()) return; + if (_requests.isEmpty()) return; int32 limit = (_locationType == UnknownFileLocation) ? DownloadPartSize : DocumentDownloadPartSize; - DataRequested &dr(_dataRequested[dc]); - for (Requests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) { + DataRequested &dr(DataRequestedMap[_dc]); + for (Requests::const_iterator i = _requests.cbegin(), e = _requests.cend(); i != e; ++i) { MTP::cancel(i.key()); int32 dcIndex = i.value(); dr.v[dcIndex] -= limit; } - queue->queries -= requests.size(); - requests.clear(); + queue->queries -= _requests.size(); + _requests.clear(); if (!queue->queries) { - App::app()->killDownloadSessionsStart(dc); + App::app()->killDownloadSessionsStart(_dc); } } void mtpFileLoader::startLoading(bool loadFirst, bool prior) { - if ((queue->queries >= MaxFileQueries && (!loadFirst || !prior)) || complete) return; + if ((queue->queries >= MaxFileQueries && (!loadFirst || !prior)) || _complete) return; loadPart(); } @@ -623,6 +580,6 @@ mtpFileLoader::~mtpFileLoader() { namespace MTP { void clearLoaderPriorities() { - ++_priority; + ++GlobalPriority; } } diff --git a/Telegram/SourceFiles/mtproto/mtpFileLoader.h b/Telegram/SourceFiles/mtproto/mtpFileLoader.h index 23a005b02..ea09f8012 100644 --- a/Telegram/SourceFiles/mtproto/mtpFileLoader.h +++ b/Telegram/SourceFiles/mtproto/mtpFileLoader.h @@ -100,7 +100,6 @@ struct StorageImageSaved { enum LocalLoadStatus { LocalNotTried, - LocalNeedsTry, LocalNotFound, LocalLoading, LocalLoaded, @@ -109,55 +108,64 @@ enum LocalLoadStatus { typedef void *TaskId; // no interface, just id +enum LoadFromCloudSetting { + LoadFromCloudOrLocal, + LoadFromLocalOnly, +}; +enum LoadToCacheSetting { + LoadToFileOnly, + LoadToCacheAsWell, +}; + struct mtpFileLoaderQueue; +class StorageImageLocation; class mtpFileLoader : public QObject, public RPCSender { Q_OBJECT public: - mtpFileLoader(int32 dc, const uint64 &volume, int32 local, const uint64 &secret, int32 size = 0); - mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, LocationType type, const QString &to, int32 size, bool todata = false); + mtpFileLoader(const StorageImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading); + mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, LocationType type, const QString &toFile, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading); bool done() const { - return complete; + return _complete; } mtpTypeId fileType() const { - return type; + return _type; } const QByteArray &bytes() const { - return data; + return _data; } QByteArray imageFormat() const; QPixmap imagePixmap() const; QString fileName() const { - return fname; + return _fname; } float64 currentProgress() const; int32 currentOffset(bool includeSkipped = false) const; int32 fullSize() const; - void setFileName(const QString &filename); // set filename for duplicateInData loader + bool setFileName(const QString &filename); // set filename for loaders to cache + void permitLoadFromCloud(); void pause(); void start(bool loadFirst = false, bool prior = true); void cancel(); bool loading() const { - return inQueue; + return _inQueue; } - bool paused() const { return _paused; } - bool started() const { - return inQueue || _paused; + return _inQueue || _paused; } - bool loadingLocal() const { return (_localStatus == LocalLoading); } - - bool localAvailable() const; + bool autoLoading() const { + return _autoLoading; + } uint64 objId() const; @@ -176,45 +184,44 @@ signals: private: mtpFileLoaderQueue *queue; - bool _paused, inQueue, complete; + bool _paused, _autoLoading, _inQueue, _complete; mutable LocalLoadStatus _localStatus; bool tryLoadLocal(); void cancelRequests(); typedef QMap Requests; - Requests requests; - int32 skippedBytes; - int32 nextRequestOffset; - bool lastComplete; + Requests _requests; + int32 _skippedBytes; + int32 _nextRequestOffset; + bool _lastComplete; void startLoading(bool loadFirst, bool prior); void removeFromQueue(); void loadNext(); - void finishFail(); + void cancel(bool failed); bool loadPart(); void partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req); bool partFailed(const RPCError &error); - int32 dc; + int32 _dc; LocationType _locationType; + const StorageImageLocation *_location; - uint64 volume; // for photo locations - int32 local; - uint64 secret; + uint64 _id; // for other locations + uint64 _access; + QFile _file; + QString _fname; + bool _fileIsOpen; - uint64 id; // for other locations - uint64 access; - QFile file; - QString fname; - bool fileIsOpen; - bool duplicateInData; + LoadToCacheSetting _toCache; + LoadFromCloudSetting _fromCloud; - QByteArray data; + QByteArray _data; - int32 size; - mtpTypeId type; + int32 _size; + mtpTypeId _type; TaskId _localTaskId; mutable QByteArray _imageFormat; @@ -222,3 +229,5 @@ private: void readImage() const; }; + +static mtpFileLoader * const CancelledFileLoader = reinterpret_cast(&SharedMemoryLocation0); diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index fbdbb6185..7189372f9 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1271,7 +1271,7 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (lnkPhoto) { _menu->addAction(lang(lng_context_open_image), this, SLOT(openContextUrl()))->setEnabled(true); } else { - if ((lnkVideo && lnkVideo->video()->loader) || (lnkAudio && lnkAudio->audio()->loadingStarted()) || (lnkDocument && lnkDocument->document()->loadingStarted())) { + if ((lnkVideo && lnkVideo->video()->loading()) || (lnkAudio && lnkAudio->audio()->loading()) || (lnkDocument && lnkDocument->document()->loading())) { _menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true); } else { if ((lnkVideo && !lnkVideo->video()->already(true).isEmpty()) || (lnkAudio && !lnkAudio->audio()->already(true).isEmpty()) || (lnkDocument && !lnkDocument->document()->already(true).isEmpty())) { @@ -1504,8 +1504,13 @@ void OverviewInner::cancelContextDownload() { VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data()); AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data()); DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data()); - mtpFileLoader *loader = lnkVideo ? lnkVideo->video()->loader : (lnkAudio ? lnkAudio->audio()->loader : (lnkDocument ? lnkDocument->document()->loader : 0)); - if (loader) loader->cancel(); + if (lnkVideo) { + lnkVideo->video()->cancel(); + } else if (lnkAudio) { + lnkAudio->audio()->cancel(); + } else if (lnkDocument) { + lnkDocument->document()->cancel(); + } } void OverviewInner::showContextInFolder() { diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp index 080e8cf38..f55b0d9f4 100644 --- a/Telegram/SourceFiles/playerwidget.cpp +++ b/Telegram/SourceFiles/playerwidget.cpp @@ -324,12 +324,8 @@ void PlayerWidget::preloadNext() { if (next) { if (HistoryDocument *document = static_cast(next->getMedia())) { DocumentData *d = document->getDocument(); - if (d->location(true).isEmpty() && d->data.isEmpty()) { - if (!d->loader) { - DocumentOpenLink::doOpen(d); - d->openOnSave = 0; - d->openOnSaveMsgId = FullMsgId(); - } + if (!d->loaded(true)) { + DocumentOpenLink::doOpen(d, ActionOnLoadNone); } } } @@ -648,12 +644,12 @@ void PlayerWidget::updateState(SongMsgId playing, AudioPlayerState playingState, float64 progress = 0.; int32 loaded; float64 loadProgress = 1.; - if (duration || !_song || !_song.song || !_song.song->loader) { + if (duration || !_song || !_song.song || !_song.song->loading()) { time = (_down == OverPlayback) ? _time : formatDurationText(display); progress = duration ? snap(float64(position) / duration, 0., 1.) : 0.; loaded = duration ? _song.song->size : 0; } else { - loaded = _song.song->loader ? _song.song->loader->currentOffset() : 0; + loaded = _song.song->loading() ? _song.song->loadOffset() : 0; time = formatDownloadText(loaded, _song.song->size); loadProgress = snap(float64(loaded) / qMax(_song.song->size, 1), 0., 1.); } diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index eb69cd000..6fc51ac85 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -195,7 +195,7 @@ DeclareRefSetting(EmojiColorVariants, EmojiVariants); RecentEmojiPack &cGetRecentEmojis(); -struct DocumentData; +class DocumentData; typedef QVector StickerPack; DeclareSetting(int32, StickersHash); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index f4d32485c..24bf6d95f 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -176,12 +176,6 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent) , _autoDownload(this, lang(lng_media_auto_settings)) -// chat background -, _backFromGallery(this, lang(lng_settings_bg_from_gallery)) -, _backFromFile(this, lang(lng_settings_bg_from_file)) -, _tileBackground(this, lang(lng_settings_bg_tile), cTileBackground()) -, _needBackgroundUpdate(false) - // local storage , _localStorageClear(this, lang(lng_local_storage_clear)) , _localStorageHeight(1) @@ -189,6 +183,12 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent) , _storageClearedWidth(st::linkFont->width(lang(lng_local_storage_cleared))) , _storageClearFailedWidth(st::linkFont->width(lang(lng_local_storage_clear_failed))) +// chat background +, _backFromGallery(this, lang(lng_settings_bg_from_gallery)) +, _backFromFile(this, lang(lng_settings_bg_from_file)) +, _tileBackground(this, lang(lng_settings_bg_tile), cTileBackground()) +, _needBackgroundUpdate(false) + // advanced , _passcodeEdit(this, lang(cHasPasscode() ? lng_passcode_change : lng_passcode_turn_on)) , _passcodeTurnOff(this, lang(lng_passcode_turn_off)) @@ -207,6 +207,8 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent) , _logOut(this, lang(lng_settings_logout), st::btnLogout) , _supportGetRequest(0) { if (self()) { + self()->photo->load(); + connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*))); @@ -294,13 +296,6 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent) connect(App::wnd(), SIGNAL(tempDirClearFailed(int)), this, SLOT(onTempDirClearFailed(int))); connect(&_autoDownload, SIGNAL(clicked()), this, SLOT(onAutoDownload())); - // chat background - if (!cChatBackground()) App::initBackground(); - updateChatBackground(); - connect(&_backFromGallery, SIGNAL(clicked()), this, SLOT(onBackFromGallery())); - connect(&_backFromFile, SIGNAL(clicked()), this, SLOT(onBackFromFile())); - connect(&_tileBackground, SIGNAL(changed()), this, SLOT(onTileBackground())); - // local storage connect(&_localStorageClear, SIGNAL(clicked()), this, SLOT(onLocalStorageClear())); switch (App::wnd()->localStorageState()) { @@ -309,6 +304,13 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent) case Window::TempDirRemoving: _storageClearState = TempDirClearing; break; } + // chat background + if (!cChatBackground()) App::initBackground(); + updateChatBackground(); + connect(&_backFromGallery, SIGNAL(clicked()), this, SLOT(onBackFromGallery())); + connect(&_backFromFile, SIGNAL(clicked()), this, SLOT(onBackFromFile())); + connect(&_tileBackground, SIGNAL(changed()), this, SLOT(onTileBackground())); + // advanced connect(&_passcodeEdit, SIGNAL(clicked()), this, SLOT(onPasscode())); connect(&_passcodeTurnOff, SIGNAL(clicked()), this, SLOT(onPasscodeOff())); @@ -553,45 +555,6 @@ void SettingsInner::paintEvent(QPaintEvent *e) { top += st::setLittleSkip; top += _autoDownload.height(); - // chat background - p.setFont(st::setHeaderFont->f); - p.setPen(st::setHeaderColor->p); - p.drawText(_left + st::setHeaderLeft, top + st::setHeaderTop + st::setHeaderFont->ascent, lang(lng_settings_section_background)); - top += st::setHeaderSkip; - - if (animateBackground) { - const QPixmap &pix = App::main()->newBackgroundThumb()->pixBlurred(st::setBackgroundSize); - - p.drawPixmap(_left, top, st::setBackgroundSize, st::setBackgroundSize, pix, 0, (pix.height() - st::setBackgroundSize) / 2, st::setBackgroundSize, st::setBackgroundSize); - - uint64 dt = getms(); - int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); - - int32 x = _left + (st::setBackgroundSize - st::mediaviewLoader.width()) / 2; - int32 y = top + (st::setBackgroundSize - st::mediaviewLoader.height()) / 2; - p.fillRect(x, y, st::mediaviewLoader.width(), st::mediaviewLoader.height(), st::photoLoaderBg->b); - - x += (st::mediaviewLoader.width() - cnt * st::mediaviewLoaderPoint.width() - (cnt - 1) * st::mediaviewLoaderSkip) / 2; - y += (st::mediaviewLoader.height() - st::mediaviewLoaderPoint.height()) / 2; - QColor c(st::white->c); - QBrush b(c); - for (int32 i = 0; i < cnt; ++i) { - t -= delta; - while (t < 0) t += period; - - float64 alpha = (t >= st::photoLoaderDuration1 + st::photoLoaderDuration2) ? 0 : ((t > st::photoLoaderDuration1 ? ((st::photoLoaderDuration1 + st::photoLoaderDuration2 - t) / st::photoLoaderDuration2) : (t / st::photoLoaderDuration1))); - c.setAlphaF(st::photoLoaderAlphaMin + alpha * (1 - st::photoLoaderAlphaMin)); - b.setColor(c); - p.fillRect(x + i * (st::mediaviewLoaderPoint.width() + st::mediaviewLoaderSkip), y, st::mediaviewLoaderPoint.width(), st::mediaviewLoaderPoint.height(), b); - } - QTimer::singleShot(AnimationTimerDelta, this, SLOT(updateBackgroundRect())); - } else { - p.drawPixmap(_left, top, _background); - } - top += st::setBackgroundSize; - top += st::setLittleSkip; - top += _tileBackground.height(); - // local storage p.setFont(st::setHeaderFont->f); p.setPen(st::setHeaderColor->p); @@ -635,6 +598,45 @@ void SettingsInner::paintEvent(QPaintEvent *e) { p.drawText(_left + st::setHeaderLeft, top + st::linkFont->ascent, lang(lng_settings_no_data_cached)); } top += _localStorageClear.height(); + + // chat background + p.setFont(st::setHeaderFont->f); + p.setPen(st::setHeaderColor->p); + p.drawText(_left + st::setHeaderLeft, top + st::setHeaderTop + st::setHeaderFont->ascent, lang(lng_settings_section_background)); + top += st::setHeaderSkip; + + if (animateBackground) { + const QPixmap &pix = App::main()->newBackgroundThumb()->pixBlurred(st::setBackgroundSize); + + p.drawPixmap(_left, top, st::setBackgroundSize, st::setBackgroundSize, pix, 0, (pix.height() - st::setBackgroundSize) / 2, st::setBackgroundSize, st::setBackgroundSize); + + uint64 dt = getms(); + int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); + + int32 x = _left + (st::setBackgroundSize - st::mediaviewLoader.width()) / 2; + int32 y = top + (st::setBackgroundSize - st::mediaviewLoader.height()) / 2; + p.fillRect(x, y, st::mediaviewLoader.width(), st::mediaviewLoader.height(), st::photoLoaderBg->b); + + x += (st::mediaviewLoader.width() - cnt * st::mediaviewLoaderPoint.width() - (cnt - 1) * st::mediaviewLoaderSkip) / 2; + y += (st::mediaviewLoader.height() - st::mediaviewLoaderPoint.height()) / 2; + QColor c(st::white->c); + QBrush b(c); + for (int32 i = 0; i < cnt; ++i) { + t -= delta; + while (t < 0) t += period; + + float64 alpha = (t >= st::photoLoaderDuration1 + st::photoLoaderDuration2) ? 0 : ((t > st::photoLoaderDuration1 ? ((st::photoLoaderDuration1 + st::photoLoaderDuration2 - t) / st::photoLoaderDuration2) : (t / st::photoLoaderDuration1))); + c.setAlphaF(st::photoLoaderAlphaMin + alpha * (1 - st::photoLoaderAlphaMin)); + b.setColor(c); + p.fillRect(x + i * (st::mediaviewLoaderPoint.width() + st::mediaviewLoaderSkip), y, st::mediaviewLoaderPoint.width(), st::mediaviewLoaderPoint.height(), b); + } + QTimer::singleShot(AnimationTimerDelta, this, SLOT(updateBackgroundRect())); + } else { + p.drawPixmap(_left, top, _background); + } + top += st::setBackgroundSize; + top += st::setLittleSkip; + top += _tileBackground.height(); } // advanced @@ -734,15 +736,6 @@ void SettingsInner::resizeEvent(QResizeEvent *e) { top += st::setLittleSkip; _autoDownload.move(_left + st::cbDefFlat.textLeft, top); top += _autoDownload.height(); - // chat background - top += st::setHeaderSkip; - _backFromGallery.move(_left + st::setBackgroundSize + st::setLittleSkip, top); - _backFromFile.move(_left + st::setBackgroundSize + st::setLittleSkip, top + _backFromGallery.height() + st::setLittleSkip); - top += st::setBackgroundSize; - - top += st::setLittleSkip; - _tileBackground.move(_left, top); top += _tileBackground.height(); - // local storage _localStorageClear.move(_left + st::setWidth - _localStorageClear.width(), top + st::setHeaderTop + st::setHeaderFont->ascent - st::linkFont->ascent); top += st::setHeaderSkip; @@ -753,6 +746,15 @@ void SettingsInner::resizeEvent(QResizeEvent *e) { _localStorageHeight = 1; } top += _localStorageClear.height(); + + // chat background + top += st::setHeaderSkip; + _backFromGallery.move(_left + st::setBackgroundSize + st::setLittleSkip, top); + _backFromFile.move(_left + st::setBackgroundSize + st::setLittleSkip, top + _backFromGallery.height() + st::setLittleSkip); + top += st::setBackgroundSize; + + top += st::setLittleSkip; + _tileBackground.move(_left, top); top += _tileBackground.height(); } // advanced @@ -1079,6 +1081,13 @@ void SettingsInner::showAll() { _autoDownload.hide(); } + // local storage + if (self() && _storageClearState == TempDirExists) { + _localStorageClear.show(); + } else { + _localStorageClear.hide(); + } + // chat background if (self()) { _backFromGallery.show(); @@ -1090,13 +1099,6 @@ void SettingsInner::showAll() { _tileBackground.hide(); } - // local storage - if (self() && _storageClearState == TempDirExists) { - _localStorageClear.show(); - } else { - _localStorageClear.hide(); - } - // advanced if (self()) { _passcodeEdit.show(); diff --git a/Telegram/SourceFiles/settingswidget.h b/Telegram/SourceFiles/settingswidget.h index f30537818..821ce2a67 100644 --- a/Telegram/SourceFiles/settingswidget.h +++ b/Telegram/SourceFiles/settingswidget.h @@ -262,18 +262,18 @@ private: TempDirClearState _tempDirClearState; LinkButton _autoDownload; - // chat background - QPixmap _background; - LinkButton _backFromGallery, _backFromFile; - FlatCheckbox _tileBackground; - bool _needBackgroundUpdate; - // local storage LinkButton _localStorageClear; int32 _localStorageHeight; int32 _storageClearingWidth, _storageClearedWidth, _storageClearFailedWidth; TempDirClearState _storageClearState; + // chat background + QPixmap _background; + LinkButton _backFromGallery, _backFromFile; + FlatCheckbox _tileBackground; + bool _needBackgroundUpdate; + // advanced LinkButton _passcodeEdit, _passcodeTurnOff, _autoLock; QString _autoLockText; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 6460e59d7..1c3d2a8c1 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -563,9 +563,97 @@ bool PtsWaiter::check(ChannelData *channel, int32 pts, int32 count) { // return return !count; } +PhotoData::PhotoData(const PhotoId &id, const uint64 &access, int32 date, const ImagePtr &thumb, const ImagePtr &medium, const ImagePtr &full) +: id(id) +, access(access) +, date(date) +, thumb(thumb) +, medium(medium) +, full(full) +, peer(0) +, uploadingData(0) { +} + +void PhotoData::automaticLoad(const HistoryItem *item) { + full->automaticLoad(item); +} + +bool PhotoData::loaded() const { + return full->loaded(); +} + +bool PhotoData::loading() const { + return full->loading(); +} + +bool PhotoData::displayLoading() const { + return full->loading() ? full->displayLoading() : uploading(); +} + +void PhotoData::cancel() { + full->cancel(); +} + +float64 PhotoData::progress() const { + return loading() ? full->progress() : (uploading() ? (float64(uploadingData->offset) / uploadingData->size) : (loaded() ? 1 : 0)); +} + +int32 PhotoData::loadOffset() const { + return full->loadOffset(); +} + +bool PhotoData::uploading() const { + return uploadingData; +} + +void PhotoData::forget() { + thumb->forget(); + replyPreview->forget(); + medium->forget(); + full->forget(); +} + +ImagePtr PhotoData::makeReplyPreview() { + if (replyPreview->isNull() && !thumb->isNull()) { + if (thumb->loaded()) { + int w = thumb->width(), h = thumb->height(); + if (w <= 0) w = 1; + if (h <= 0) h = 1; + replyPreview = ImagePtr(w > h ? thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : thumb->pix(st::msgReplyBarSize.height()), "PNG"); + } else { + thumb->load(); + } + } + return replyPreview; +} + +PhotoData::~PhotoData() { + delete uploadingData; + setBadPointer(uploadingData); +} + void PhotoLink::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton) { - App::wnd()->showPhoto(this, App::hoveredLinkItem()); + App::wnd()->showPhoto(this, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); + } +} + +void PhotoCancelLink::onClick(Qt::MouseButton button) const { + if (button != Qt::LeftButton) return; + + PhotoData *data = photo(); + if (!data->date) return; + + if (data->uploading()) { + HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); + if (HistoryMessage *msg = item->toHistoryMessage()) { + if (msg->getMedia() && msg->getMedia()->type() == MediaTypePhoto && static_cast(msg->getMedia())->photo() == data) { + App::contextItem(item); + App::main()->deleteLayer(-2); + } + } + } else { + data->cancel(); } } @@ -657,31 +745,37 @@ QString saveFileName(const QString &title, const QString &filter, const QString } void VideoOpenLink::onClick(Qt::MouseButton button) const { + if (button != Qt::LeftButton) return; VideoData *data = video(); - if (!data->date || button != Qt::LeftButton) return; - QString already = data->already(true); - if (!already.isEmpty()) { - psOpenFile(already); + if (!data->date) return; + + HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); + + const FileLocation &location(data->location(true)); + if (!location.isEmpty()) { + psOpenFile(location.name()); if (App::main()) App::main()->videoMarkRead(data); return; } if (data->status != FileReady) return; - QString filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), qsl(".mov"), false); - if (!filename.isEmpty()) { - data->openOnSave = 1; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); - data->save(filename); + QString filename; + if (!data->saveToCache()) { + filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), qsl(".mov"), false); + if (filename.isEmpty()) return; } + + data->save(filename, ActionOnLoadOpen, item ? item->fullId() : FullMsgId()); } void VideoSaveLink::doSave(VideoData *data, bool forceSavingAs) { if (!data->date) return; QString already = data->already(true); - if (!already.isEmpty() && !forceSavingAs) { + bool openWith = !already.isEmpty(); + if (openWith && !forceSavingAs) { QPoint pos(QCursor::pos()); if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) { psOpenFile(already, true); @@ -692,13 +786,9 @@ void VideoSaveLink::doSave(VideoData *data, bool forceSavingAs) { QString name = already.isEmpty() ? QString(".mov") : alreadyInfo.fileName(); QString filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), name, forceSavingAs, alreadyDir); if (!filename.isEmpty()) { - if (forceSavingAs) { - data->cancel(); - } else if (!already.isEmpty()) { - data->openOnSave = -1; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : FullMsgId(); - } - data->save(filename); + ActionOnLoad action = already.isEmpty() ? ActionOnLoadNone : ActionOnLoadOpenWith; + FullMsgId actionMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); + data->save(filename, action, actionMsgId); } } } @@ -715,21 +805,136 @@ void VideoCancelLink::onClick(Qt::MouseButton button) const { data->cancel(); } -VideoData::VideoData(const VideoId &id, const uint64 &access, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) : -id(id), access(access), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), loader(0) { +VideoData::VideoData(const VideoId &id, const uint64 &access, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) +: id(id) +, access(access) +, date(date) +, duration(duration) +, w(w) +, h(h) +, thumb(thumb) +, dc(dc) +, size(size) +, status(FileReady) +, uploadOffset(0) +, _actionOnLoad(ActionOnLoadNone) +, _loader(0) { _location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id)); } -void VideoData::save(const QString &toFile) { - cancel(true); - loader = new mtpFileLoader(dc, id, access, VideoFileLocation, toFile, size); - loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(videoLoadProgress(mtpFileLoader*))); - loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(videoLoadFailed(mtpFileLoader*, bool))); - loader->start(); +void VideoData::forget() { + replyPreview->forget(); + thumb->forget(); +} + +void VideoData::performActionOnLoad() { + if (_actionOnLoad == ActionOnLoadNone) return; + + const FileLocation &loc(location(true)); + QString already = loc.name(); + if (already.isEmpty()) return; + + if (_actionOnLoad == ActionOnLoadOpenWith) { + QPoint pos(QCursor::pos()); + if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) { + psOpenFile(already, true); + } + } else if (_actionOnLoad == ActionOnLoadOpen || _actionOnLoad == ActionOnLoadPlayInline) { + psOpenFile(already); + } + _actionOnLoad = ActionOnLoadNone; +} + +bool VideoData::loaded(bool check) const { + if (loading() && _loader->done()) { + if (_loader->fileType() == mtpc_storage_fileUnknown) { + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = CancelledFileLoader; + } else { + VideoData *that = const_cast(this); + that->_location = FileLocation(mtpToStorageType(_loader->fileType()), _loader->fileName()); + + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = 0; + } + notifyLayoutChanged(); + } + return !already(check).isEmpty(); +} + +bool VideoData::loading() const { + return _loader && _loader != CancelledFileLoader; +} + +bool VideoData::displayLoading() const { + return loading() ? (!_loader->loadingLocal() || !_loader->autoLoading()) : uploading(); +} + +float64 VideoData::progress() const { + return loading() ? _loader->currentProgress() : (uploading() ? (float64(uploadOffset) / size) : (loaded() ? 1 : 0)); +} + +int32 VideoData::loadOffset() const { + return loading() ? _loader->currentOffset() : 0; +} + +bool VideoData::uploading() const { + return status == FileUploading; +} + +void VideoData::save(const QString &toFile, ActionOnLoad action, const FullMsgId &actionMsgId, LoadFromCloudSetting fromCloud, bool autoLoading) { + if (loaded(true)) { + const FileLocation &l(location(true)); + if (!toFile.isEmpty()) { + if (l.accessEnable()) { + QFile(l.name()).copy(toFile); + l.accessDisable(); + } + } + return; + } + + if (_loader == CancelledFileLoader) _loader = 0; + if (_loader) { + if (!_loader->setFileName(toFile)) { + cancel(); + _loader = 0; + } + } + + _actionOnLoad = action; + _actionOnLoadMsgId = actionMsgId; + + if (_loader) { + if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud(); + } else { + status = FileReady; + _loader = new mtpFileLoader(dc, id, access, VideoFileLocation, toFile, size, (saveToCache() ? LoadToCacheAsWell : LoadToFileOnly), fromCloud, autoLoading); + _loader->connect(_loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(videoLoadProgress(mtpFileLoader*))); + _loader->connect(_loader, SIGNAL(failed(mtpFileLoader*,bool)), App::main(), SLOT(videoLoadProgress(mtpFileLoader*,bool))); + _loader->start(); + } notifyLayoutChanged(); } +void VideoData::cancel() { + if (!loading()) return; + + mtpFileLoader *l = _loader; + _loader = CancelledFileLoader; + if (l) { + l->cancel(); + l->deleteLater(); + l->rpcInvalidate(); + + notifyLayoutChanged(); + } + _actionOnLoad = ActionOnLoadNone; +} + void VideoData::notifyLayoutChanged() const { const VideoItems &items(App::videoItems()); VideoItems::const_iterator i = items.constFind(const_cast(this)); @@ -740,34 +945,54 @@ void VideoData::notifyLayoutChanged() const { } } -QString VideoData::already(bool check) { +QString VideoData::already(bool check) const { return location(check).name(); } -const FileLocation &VideoData::location(bool check) { - if (check && !_location.check()) _location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id)); +QByteArray VideoData::data() const { + return QByteArray(); +} + +const FileLocation &VideoData::location(bool check) const { + if (check && !_location.check()) { + const_cast(this)->_location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id)); + } return _location; } -void AudioOpenLink::onClick(Qt::MouseButton button) const { - AudioData *data = audio(); - if (!data->date || button != Qt::LeftButton) return; +void VideoData::setLocation(const FileLocation &loc) { + if (loc.check()) { + _location = loc; + } +} - QString already = data->already(true); - bool play = App::hoveredLinkItem() && audioPlayer(); - if (!already.isEmpty() || (!data->data.isEmpty() && play)) { +void AudioOpenLink::onClick(Qt::MouseButton button) const { + if (button != Qt::LeftButton) return; + AudioData *data = audio(); + + if (!data->date) return; + + HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); + + bool play = audioPlayer() && item; + const FileLocation &location(data->location(true)); + if (!location.isEmpty() || (!data->data().isEmpty() && play)) { if (play) { AudioMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; audioPlayer()->currentState(&playing, &playingState); - if (playing.msgId == App::hoveredLinkItem()->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { + if (playing.msgId == item->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { audioPlayer()->pauseresume(OverviewAudios); } else { - audioPlayer()->play(AudioMsgId(data, App::hoveredLinkItem()->fullId())); - if (App::main()) App::main()->audioMarkRead(data); + AudioMsgId audio(data, item->fullId()); + audioPlayer()->play(audio); + if (App::main()) { + App::main()->audioPlayProgress(audio); + App::main()->audioMarkRead(data); + } } } else { - psOpenFile(already); + psOpenFile(location.name()); if (App::main()) App::main()->audioMarkRead(data); } return; @@ -775,31 +1000,23 @@ void AudioOpenLink::onClick(Qt::MouseButton button) const { if (data->status != FileReady) return; - if (data->prepareAutoLoader(App::hoveredLinkItem(), true)) { - data->openOnSave = 1; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); - if (!data->loader->started()) { - data->save(QString()); - } else { - data->loader->start(); - } - return; + QString filename; + if (!data->saveToCache()) { + bool mp3 = (data->mime == qstr("audio/mp3")); + filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), mp3 ? qsl(".mp3") : qsl(".ogg"), false); + + if (filename.isEmpty()) return; } - bool mp3 = (data->mime == qstr("audio/mp3")); - QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), mp3 ? qsl(".mp3") : qsl(".ogg"), false); - if (!filename.isEmpty()) { - data->openOnSave = 1; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); - data->save(filename); - } + data->save(filename, ActionOnLoadOpen, item ? item->fullId() : FullMsgId()); } void AudioSaveLink::doSave(AudioData *data, bool forceSavingAs) { if (!data->date) return; QString already = data->already(true); - if (!already.isEmpty() && !forceSavingAs) { + bool openWith = !already.isEmpty(); + if (openWith && !forceSavingAs) { QPoint pos(QCursor::pos()); if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) { psOpenFile(already, true); @@ -811,13 +1028,9 @@ void AudioSaveLink::doSave(AudioData *data, bool forceSavingAs) { QString name = already.isEmpty() ? (mp3 ? qsl(".mp3") : qsl(".ogg")) : alreadyInfo.fileName(); QString filename = saveFileName(lang(lng_save_audio), mp3 ? qsl("MP3 Audio (*.mp3);;All files (*.*)") : qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), name, forceSavingAs, alreadyDir); if (!filename.isEmpty()) { - if (forceSavingAs) { - data->cancel(); - } else if (!already.isEmpty()) { - data->openOnSave = -1; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); - } - data->save(filename); + ActionOnLoad action = already.isEmpty() ? ActionOnLoadNone : ActionOnLoadOpenWith; + FullMsgId actionMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); + data->save(filename, action, actionMsgId); } } } @@ -831,7 +1044,17 @@ void AudioCancelLink::onClick(Qt::MouseButton button) const { AudioData *data = audio(); if (!data->date || button != Qt::LeftButton) return; - data->cancel(); + if (data->uploading()) { + HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); + if (HistoryMessage *msg = item->toHistoryMessage()) { + if (msg->getMedia() && msg->getMedia()->type() == MediaTypeAudio && static_cast(msg->getMedia())->audio() == data) { + App::contextItem(item); + App::main()->deleteLayer(-2); + } + } + } else { + data->cancel(); + } } bool StickerData::setInstalled() const { @@ -852,42 +1075,177 @@ bool StickerData::setInstalled() const { return false; } -AudioData::AudioData(const AudioId &id, const uint64 &access, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) : -id(id), access(access), date(date), mime(mime), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0) { +AudioData::AudioData(const AudioId &id, const uint64 &access, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) +: id(id) +, access(access) +, date(date) +, mime(mime) +, duration(duration) +, dc(dc) +, size(size) +, status(FileReady) +, uploadOffset(0) +, _actionOnLoad(ActionOnLoadNone) +, _loader(0) { _location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id)); } -bool AudioData::prepareAutoLoader(const HistoryItem *item, bool force) { - if (!loader && !loaded(true) && size < AudioVoiceMsgInMemory) { - bool load = force; - if (!force) { - if (item->history()->peer->isUser()) { - load = !(cAutoDownloadAudio() & dbiadNoPrivate); - } else { - load = !(cAutoDownloadAudio() & dbiadNoGroups); - } - } - if (load || Local::willAudioLoad(mediaKey(AudioFileLocation, dc, id), true)) { - loader = new mtpFileLoader(dc, id, access, AudioFileLocation, QString(), size, true); - } - } - return loader; +bool AudioData::saveToCache() const { + return size < AudioVoiceMsgInMemory; } -void AudioData::save(const QString &toFile) { - if (loader && loader->fileName().isEmpty()) { - loader->setFileName(toFile); - } else { - cancel(true); - loader = new mtpFileLoader(dc, id, access, AudioFileLocation, toFile, size, (size < AudioVoiceMsgInMemory)); +void AudioData::forget() { + _data.clear(); +} + +void AudioData::automaticLoad(const HistoryItem *item) { + if (loaded() || status != FileReady) return; + + if (saveToCache() && _loader != CancelledFileLoader) { + if (item) { + bool loadFromCloud = false; + if (item->history()->peer->isUser()) { + loadFromCloud = !(cAutoDownloadAudio() & dbiadNoPrivate); + } else { + loadFromCloud = !(cAutoDownloadAudio() & dbiadNoGroups); + } + save(QString(), _actionOnLoad, _actionOnLoadMsgId, loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true); + } + } +} + +void AudioData::performActionOnLoad() { + if (_actionOnLoad == ActionOnLoadNone) return; + + const FileLocation &loc(location(true)); + QString already = loc.name(); + bool play = _actionOnLoadMsgId.msg && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && audioPlayer(); + + if (play) { + if (loaded()) { + AudioMsgId playing; + AudioPlayerState state = AudioPlayerStopped; + audioPlayer()->currentState(&playing, &state); + if (playing.msgId == _actionOnLoadMsgId && !(state & AudioPlayerStoppedMask) && state != AudioPlayerFinishing) { + audioPlayer()->pauseresume(OverviewAudios); + } else { + audioPlayer()->play(AudioMsgId(this, _actionOnLoadMsgId)); + if (App::main()) App::main()->audioMarkRead(this); + } + } + } else { + if (already.isEmpty()) return; + if (_actionOnLoad == ActionOnLoadOpenWith) { + if (already.isEmpty()) return; + + QPoint pos(QCursor::pos()); + if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) { + psOpenFile(already, true); + } + if (App::main()) App::main()->audioMarkRead(this); + } else if (_actionOnLoad == ActionOnLoadOpen || _actionOnLoad == ActionOnLoadPlayInline) { + psOpenFile(already); + if (App::main()) App::main()->audioMarkRead(this); + } + } + _actionOnLoad = ActionOnLoadNone; +} + +bool AudioData::loaded(bool check) const { + if (loading() && _loader->done()) { + if (_loader->fileType() == mtpc_storage_fileUnknown) { + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = CancelledFileLoader; + } else { + AudioData *that = const_cast(this); + that->_location = FileLocation(mtpToStorageType(_loader->fileType()), _loader->fileName()); + that->_data = _loader->bytes(); + + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = 0; + } + notifyLayoutChanged(); + } + return !_data.isEmpty() || !already(check).isEmpty(); +} + +bool AudioData::loading() const { + return _loader && _loader != CancelledFileLoader; +} + +bool AudioData::displayLoading() const { + return loading() ? (!_loader->loadingLocal() || !_loader->autoLoading()) : uploading(); +} + +float64 AudioData::progress() const { + return loading() ? _loader->currentProgress() : (uploading() ? (float64(uploadOffset) / size) : (loaded() ? 1 : 0)); +} + +int32 AudioData::loadOffset() const { + return loading() ? _loader->currentOffset() : 0; +} + +bool AudioData::uploading() const { + return status == FileUploading; +} + +void AudioData::save(const QString &toFile, ActionOnLoad action, const FullMsgId &actionMsgId, LoadFromCloudSetting fromCloud, bool autoLoading) { + if (loaded(true)) { + const FileLocation &l(location(true)); + if (!toFile.isEmpty()) { + if (!_data.isEmpty()) { + QFile f(toFile); + f.open(QIODevice::WriteOnly); + f.write(_data); + } else if (l.accessEnable()) { + QFile(l.name()).copy(toFile); + l.accessDisable(); + } + } + return; + } + + if (_loader == CancelledFileLoader) _loader = 0; + if (_loader) { + if (!_loader->setFileName(toFile)) { + cancel(); + _loader = 0; + } + } + + _actionOnLoad = action; + _actionOnLoadMsgId = actionMsgId; + + if (_loader) { + if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud(); + } else { + status = FileReady; + _loader = new mtpFileLoader(dc, id, access, AudioFileLocation, toFile, size, (saveToCache() ? LoadToCacheAsWell : LoadToFileOnly), fromCloud, autoLoading); + _loader->connect(_loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(audioLoadProgress(mtpFileLoader*))); + _loader->connect(_loader, SIGNAL(failed(mtpFileLoader*,bool)), App::main(), SLOT(audioLoadFailed(mtpFileLoader*,bool))); + _loader->start(); } - loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(audioLoadProgress(mtpFileLoader*))); - loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(audioLoadFailed(mtpFileLoader*, bool))); - loader->start(); notifyLayoutChanged(); } +void AudioData::cancel() { + if (!loading()) return; + + mtpFileLoader *l = _loader; + _loader = CancelledFileLoader; + if (l) { + l->cancel(); + l->deleteLater(); + l->rpcInvalidate(); + + notifyLayoutChanged(); + } + _actionOnLoad = ActionOnLoadNone; +} + void AudioData::notifyLayoutChanged() const { const AudioItems &items(App::audioItems()); AudioItems::const_iterator i = items.constFind(const_cast(this)); @@ -898,43 +1256,57 @@ void AudioData::notifyLayoutChanged() const { } } -QString AudioData::already(bool check) { +QString AudioData::already(bool check) const { return location(check).name(); } -const FileLocation &AudioData::location(bool check) { - if (check && !_location.check()) _location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id)); +QByteArray AudioData::data() const { + return _data; +} + +const FileLocation &AudioData::location(bool check) const { + if (check && !_location.check()) { + const_cast(this)->_location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id)); + } return _location; } -void DocumentOpenLink::doOpen(DocumentData *data, int32 openOnSave) { +void AudioData::setLocation(const FileLocation &loc) { + if (loc.check()) { + _location = loc; + } +} + +void DocumentOpenLink::doOpen(DocumentData *data, ActionOnLoad action) { if (!data->date) return; - bool playMusic = data->song() && audioPlayer() && App::hoveredLinkItem(); - bool playAnimation = data->isAnimation() && App::hoveredLinkItem() && App::hoveredLinkItem()->getMedia(); + HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); + + bool playMusic = data->song() && audioPlayer() && item; + bool playAnimation = data->isAnimation() && item && item->getMedia(); const FileLocation &location(data->location(true)); - if (!location.isEmpty() || (!data->data.isEmpty() && (playMusic || playAnimation))) { + if (!location.isEmpty() || (!data->data().isEmpty() && (playMusic || playAnimation))) { if (playMusic) { SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; audioPlayer()->currentState(&playing, &playingState); - if (playing.msgId == App::hoveredLinkItem()->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { + if (playing.msgId == item->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { audioPlayer()->pauseresume(OverviewDocuments); } else { - SongMsgId song(data, App::hoveredLinkItem()->fullId()); + SongMsgId song(data, item->fullId()); audioPlayer()->play(song); if (App::main()) App::main()->documentPlayProgress(song); } } else if (data->size < MediaViewImageSizeLimit) { - if (!data->data.isEmpty() && playAnimation) { - if (openOnSave > 1) { - App::hoveredLinkItem()->getMedia()->playInline(App::hoveredLinkItem()); + if (!data->data().isEmpty() && playAnimation) { + if (action == ActionOnLoadPlayInline) { + item->getMedia()->playInline(item); } else { - App::wnd()->showDocument(data, App::hoveredLinkItem()); + App::wnd()->showDocument(data, item); } } else if (location.accessEnable()) { if ((App::hoveredLinkItem() || App::contextItem()) && (data->isAnimation() || QImageReader(location.name()).canRead())) { - App::wnd()->showDocument(data, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); + App::wnd()->showDocument(data, item); } else { psOpenFile(location.name()); } @@ -950,37 +1322,28 @@ void DocumentOpenLink::doOpen(DocumentData *data, int32 openOnSave) { if (data->status != FileReady) return; - if (data->prepareAutoLoader(App::hoveredLinkItem(), true)) { - data->openOnSave = openOnSave; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); - if (!data->loader->started()) { - data->save(QString()); - } else { - data->loader->start(); + QString filename; + if (!data->saveToCache()) { + QString name = data->name, filter; + MimeType mimeType = mimeTypeForName(data->mime); + QStringList p = mimeType.globPatterns(); + QString pattern = p.isEmpty() ? QString() : p.front(); + if (name.isEmpty()) { + name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString()); } - return; + + if (pattern.isEmpty()) { + filter = QString(); + } else { + filter = mimeType.filterString() + qsl(";;All files (*.*)"); + } + + filename = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, false); + + if (filename.isEmpty()) return; } - QString name = data->name, filter; - MimeType mimeType = mimeTypeForName(data->mime); - QStringList p = mimeType.globPatterns(); - QString pattern = p.isEmpty() ? QString() : p.front(); - if (name.isEmpty()) { - name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString()); - } - - if (pattern.isEmpty()) { - filter = QString(); - } else { - filter = mimeType.filterString() + qsl(";;All files (*.*)"); - } - - QString filename = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, false); - if (!filename.isEmpty()) { - data->openOnSave = openOnSave; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); - data->save(filename); - } + data->save(filename, action, item ? item->fullId() : FullMsgId()); } void DocumentOpenLink::onClick(Qt::MouseButton button) const { @@ -989,24 +1352,7 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const { } void GifOpenLink::doOpen(DocumentData *data) { - if (!data->date) return; - - const FileLocation &location(data->location(true)); - if (!location.isEmpty()) { - if (data->size < MediaViewImageSizeLimit && location.accessEnable()) { - if (!App::hoveredLinkItem() || !App::hoveredLinkItem()->getMedia() || !App::hoveredLinkItem()->getMedia()->playInline(App::hoveredLinkItem())) { - psOpenFile(location.name()); - } - location.accessDisable(); - } else { - psOpenFile(location.name()); - } - return; - } - - if (data->status != FileReady) return; - - return DocumentOpenLink::doOpen(data, 2); + return DocumentOpenLink::doOpen(data, ActionOnLoadPlayInline); } void GifOpenLink::onClick(Qt::MouseButton button) const { @@ -1018,7 +1364,8 @@ void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) { if (!data->date) return; QString already = data->already(true); - if (!already.isEmpty() && !forceSavingAs) { + bool openWith = !already.isEmpty(); + if (openWith && !forceSavingAs) { QPoint pos(QCursor::pos()); if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) { psOpenFile(already, true); @@ -1042,13 +1389,9 @@ void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) { QString filename = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, forceSavingAs, alreadyDir); if (!filename.isEmpty()) { - if (forceSavingAs) { - data->cancel(); - } else if (!already.isEmpty()) { - data->openOnSave = -1; - data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); - } - data->save(filename); + ActionOnLoad action = already.isEmpty() ? ActionOnLoadNone : ActionOnLoadOpenWith; + FullMsgId actionMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->fullId() : (App::contextItem() ? App::contextItem()->fullId() : FullMsgId()); + data->save(filename, action, actionMsgId); } } } @@ -1059,10 +1402,22 @@ void DocumentSaveLink::onClick(Qt::MouseButton button) const { } void DocumentCancelLink::onClick(Qt::MouseButton button) const { - DocumentData *data = document(); - if (!data->date || button != Qt::LeftButton) return; + if (button != Qt::LeftButton) return; - data->cancel(); + DocumentData *data = document(); + if (!data->date) return; + + if (data->uploading()) { + HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); + if (HistoryMessage *msg = item->toHistoryMessage()) { + if (msg->getMedia() && msg->getMedia()->getDocument() == data) { + App::contextItem(item); + App::main()->deleteLayer(-2); + } + } + } else { + data->cancel(); + } } DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) : id(id) @@ -1075,10 +1430,10 @@ DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 dat , size(size) , status(FileReady) , uploadOffset(0) -, openOnSave(0) -, loader(0) , _additional(0) -, _isImage(false) { +, _isImage(false) +, _actionOnLoad(ActionOnLoadNone) +, _loader(0) { _location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); setattributes(attributes); } @@ -1134,41 +1489,189 @@ void DocumentData::setattributes(const QVector &attributes } } -bool DocumentData::prepareAutoLoader(const HistoryItem *item, bool force) { - if (!loader && !loaded(true)) { +bool DocumentData::saveToCache() const { + return (type == StickerDocument) || (isAnimation() && size < AnimationInMemory); +} + +void DocumentData::forget() { + thumb->forget(); + if (sticker()) sticker()->img->forget(); + replyPreview->forget(); + _data.clear(); +} + +void DocumentData::automaticLoad(const HistoryItem *item) { + if (loaded() || status != FileReady) return; + + if (saveToCache() && _loader != CancelledFileLoader) { if (type == StickerDocument) { - loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, QString(), size, true); - } else if (isAnimation() && item && size <= AnimationInMemory) { - bool load = force; - if (!load) { - if (item->history()->peer->isUser()) { - load = !(cAutoDownloadGif() & dbiadNoPrivate); - } else { - load = !(cAutoDownloadGif() & dbiadNoGroups); - } + save(QString(), _actionOnLoad, _actionOnLoadMsgId); + } else if (isAnimation() && item) { + bool loadFromCloud = false; + if (item->history()->peer->isUser()) { + loadFromCloud = !(cAutoDownloadGif() & dbiadNoPrivate); + } else { + loadFromCloud = !(cAutoDownloadGif() & dbiadNoGroups); } - if (load || Local::willStickerImageLoad(mediaKey(DocumentFileLocation, dc, id), true)) { - loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, QString(), size, true); + save(QString(), _actionOnLoad, _actionOnLoadMsgId, loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true); + } + } +} + +void DocumentData::performActionOnLoad() { + if (_actionOnLoad == ActionOnLoadNone) return; + + const FileLocation &loc(location(true)); + QString already = loc.name(); + HistoryItem *item = _actionOnLoadMsgId.msg ? App::histItemById(_actionOnLoadMsgId) : 0; + bool showImage = item && (size < MediaViewImageSizeLimit); + bool playMusic = song() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && item; + bool playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item->getMedia(); + if (playMusic) { + if (loaded()) { + SongMsgId playing; + AudioPlayerState playingState = AudioPlayerStopped; + audioPlayer()->currentState(&playing, &playingState); + if (playing.msgId == item->fullId() && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { + audioPlayer()->pauseresume(OverviewDocuments); + } else { + SongMsgId song(this, item->fullId()); + audioPlayer()->play(song); + if (App::main()) App::main()->documentPlayProgress(song); + } + } + } else if (playAnimation) { + if (loaded()) { + if (_actionOnLoad == ActionOnLoadPlayInline) { + item->getMedia()->playInline(item); + } else { + App::wnd()->showDocument(this, item); + } + } + } else { + if (already.isEmpty()) return; + + if (_actionOnLoad == ActionOnLoadOpenWith) { + if (already.isEmpty()) return; + + QPoint pos(QCursor::pos()); + if (!psShowOpenWithMenu(pos.x(), pos.y(), already)) { + psOpenFile(already, true); + } + } else if (_actionOnLoad == ActionOnLoadOpen || _actionOnLoad == ActionOnLoadPlayInline) { + if (loc.accessEnable()) { + if (showImage && QImageReader(loc.name()).canRead()) { + App::wnd()->showDocument(this, item); + } else { + psOpenFile(already); + } + loc.accessDisable(); + } else { + psOpenFile(already); } } } - return loader; + _actionOnLoad = ActionOnLoadNone; } -void DocumentData::save(const QString &toFile) { - if (loader && loader->fileName().isEmpty()) { - loader->setFileName(toFile); - } else if (!loader || !toFile.isEmpty()) { - cancel(true); - loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, toFile, size, (type == StickerDocument)); +bool DocumentData::loaded(bool check) const { + if (loading() && _loader->done()) { + if (_loader->fileType() == mtpc_storage_fileUnknown) { + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = CancelledFileLoader; + } else { + DocumentData *that = const_cast(this); + that->_location = FileLocation(mtpToStorageType(_loader->fileType()), _loader->fileName()); + that->_data = _loader->bytes(); + if (that->sticker() && !_loader->imagePixmap().isNull()) { + that->sticker()->img = ImagePtr(_data, _loader->imageFormat(), _loader->imagePixmap()); + } + + _loader->deleteLater(); + _loader->rpcInvalidate(); + _loader = 0; + } + notifyLayoutChanged(); + } + return !_data.isEmpty() || !already(check).isEmpty(); +} + +bool DocumentData::loading() const { + return _loader && _loader != CancelledFileLoader; +} + +bool DocumentData::displayLoading() const { + return loading() ? (!_loader->loadingLocal() || !_loader->autoLoading()) : uploading(); +} + +float64 DocumentData::progress() const { + return loading() ? _loader->currentProgress() : (uploading() ? (float64(uploadOffset) / size) : (loaded() ? 1 : 0)); +} + +int32 DocumentData::loadOffset() const { + return loading() ? _loader->currentOffset() : 0; +} + +bool DocumentData::uploading() const { + return status == FileUploading; +} + +void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMsgId &actionMsgId, LoadFromCloudSetting fromCloud, bool autoLoading) { + if (loaded(true)) { + const FileLocation &l(location(true)); + if (!toFile.isEmpty()) { + if (!_data.isEmpty()) { + QFile f(toFile); + f.open(QIODevice::WriteOnly); + f.write(_data); + } else if (l.accessEnable()) { + QFile(l.name()).copy(toFile); + l.accessDisable(); + } + } + return; + } + + if (_loader == CancelledFileLoader) _loader = 0; + if (_loader) { + if (!_loader->setFileName(toFile)) { + cancel(); + _loader = 0; + } + } + + _actionOnLoad = action; + _actionOnLoadMsgId = actionMsgId; + + if (_loader) { + if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud(); + } else { + status = FileReady; + _loader = new mtpFileLoader(dc, id, access, DocumentFileLocation, toFile, size, (saveToCache() ? LoadToCacheAsWell : LoadToFileOnly), fromCloud, autoLoading); + _loader->connect(_loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(documentLoadProgress(mtpFileLoader*))); + _loader->connect(_loader, SIGNAL(failed(mtpFileLoader*,bool)), App::main(), SLOT(documentLoadFailed(mtpFileLoader*,bool))); + _loader->start(); } - loader->connect(loader, SIGNAL(progress(mtpFileLoader*)), App::main(), SLOT(documentLoadProgress(mtpFileLoader*))); - loader->connect(loader, SIGNAL(failed(mtpFileLoader*, bool)), App::main(), SLOT(documentLoadFailed(mtpFileLoader*, bool))); - loader->start(); notifyLayoutChanged(); } +void DocumentData::cancel() { + if (!loading()) return; + + mtpFileLoader *l = _loader; + _loader = CancelledFileLoader; + if (l) { + l->cancel(); + l->deleteLater(); + l->rpcInvalidate(); + + notifyLayoutChanged(); + } + _actionOnLoad = ActionOnLoadNone; +} + void DocumentData::notifyLayoutChanged() const { const DocumentItems &items(App::documentItems()); DocumentItems::const_iterator i = items.constFind(const_cast(this)); @@ -1179,15 +1682,42 @@ void DocumentData::notifyLayoutChanged() const { } } -QString DocumentData::already(bool check) { +QString DocumentData::already(bool check) const { + if (check && _location.name().isEmpty()) return QString(); return location(check).name(); } -const FileLocation &DocumentData::location(bool check) { - if (check && !_location.check()) _location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); +QByteArray DocumentData::data() const { + return _data; +} + +const FileLocation &DocumentData::location(bool check) const { + if (check && !_location.check()) { + const_cast(this)->_location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); + } return _location; } +void DocumentData::setLocation(const FileLocation &loc) { + if (loc.check()) { + _location = loc; + } +} + +ImagePtr DocumentData::makeReplyPreview() { + if (replyPreview->isNull() && !thumb->isNull()) { + if (thumb->loaded()) { + int w = thumb->width(), h = thumb->height(); + if (w <= 0) w = 1; + if (h <= 0) h = 1; + replyPreview = ImagePtr(w > h ? thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : thumb->pix(st::msgReplyBarSize.height()), "PNG"); + } else { + thumb->load(); + } + } + return replyPreview; +} + bool fileIsImage(const QString &name, const QString &mime) { QString lowermime = mime.toLower(), namelower = name.toLower(); if (lowermime.startsWith(qstr("image/"))) { @@ -1211,6 +1741,10 @@ void DocumentData::recountIsImage() { _isImage = fileIsImage(name, mime); } +DocumentData::~DocumentData() { + delete _additional; +} + WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill) : id(id) , type(type) , url(url) diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 34cba1b60..5dfc7117c 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -340,7 +340,7 @@ enum UserBlockedStatus { UserIsNotBlocked, }; -struct PhotoData; +class PhotoData; class UserData : public PeerData { public: @@ -720,30 +720,33 @@ inline int32 newMessageFlags(PeerData *p) { return p->isSelf() ? 0 : (((p->isChat() || (p->isUser() && !p->asUser()->botInfo)) ? MTPDmessage::flag_unread : 0) | MTPDmessage::flag_out); } +enum ActionOnLoad { + ActionOnLoadNone, + ActionOnLoadOpen, + ActionOnLoadOpenWith, + ActionOnLoadPlayInline +}; + typedef QMap PreparedPhotoThumbs; -struct PhotoData { - PhotoData(const PhotoId &id, const uint64 &access = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr()) : - id(id), access(access), date(date), thumb(thumb), medium(medium), full(full), peer(0) { - } - void forget() { - thumb->forget(); - replyPreview->forget(); - medium->forget(); - full->forget(); - } - ImagePtr makeReplyPreview() { - if (replyPreview->isNull() && !thumb->isNull()) { - if (thumb->loaded()) { - int w = thumb->width(), h = thumb->height(); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - replyPreview = ImagePtr(w > h ? thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : thumb->pix(st::msgReplyBarSize.height()), "PNG"); - } else { - thumb->load(); - } - } - return replyPreview; - } +class PhotoData { +public: + PhotoData(const PhotoId &id, const uint64 &access = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr()); + + void automaticLoad(const HistoryItem *item); + + bool loaded() const; + bool loading() const; + bool displayLoading() const; + void cancel(); + float64 progress() const; + int32 loadOffset() const; + bool uploading() const; + + void forget(); + ImagePtr makeReplyPreview(); + + ~PhotoData(); + PhotoId id; uint64 access; int32 date; @@ -754,17 +757,22 @@ struct PhotoData { PeerData *peer; // for chat and channel photos connection // geo, caption - int32 cachew; - QPixmap cache; + struct UploadingData { + UploadingData(int32 size) : offset(0), size(size) { + } + int32 offset, size; + }; + UploadingData *uploadingData; + +// int32 cachew; +// QPixmap cache; }; class PhotoLink : public ITextLink { TEXT_LINK_CLASS(PhotoLink) public: - PhotoLink(PhotoData *photo) : _photo(photo), _peer(0) { - } - PhotoLink(PhotoData *photo, PeerData *peer) : _photo(photo), _peer(peer) { + PhotoLink(PhotoData *photo, PeerData *peer = 0) : _photo(photo), _peer(peer) { } void onClick(Qt::MouseButton button) const; PhotoData *photo() const { @@ -777,6 +785,17 @@ public: private: PhotoData *_photo; PeerData *_peer; + +}; + +class PhotoCancelLink : public PhotoLink { + TEXT_LINK_CLASS(PhotoCancelLink) + +public: + PhotoCancelLink(PhotoData *photo, PeerData *peer = 0) : PhotoLink(photo, peer) { + } + void onClick(Qt::MouseButton button) const; + }; enum FileStatus { @@ -786,52 +805,34 @@ enum FileStatus { FileReady = 1, }; -struct VideoData { +class VideoData { +public: VideoData(const VideoId &id, const uint64 &access = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0); - void forget() { - thumb->forget(); - replyPreview->forget(); + void automaticLoad(const HistoryItem *item) { } - void save(const QString &toFile); + bool loaded(bool check = false) const; + bool loading() const; + bool displayLoading() const; + void save(const QString &toFile, ActionOnLoad action = ActionOnLoadNone, const FullMsgId &actionMsgId = FullMsgId(), LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, bool autoLoading = false); + void cancel(); + float64 progress() const; + int32 loadOffset() const; + bool uploading() const; - void cancel(bool beforeDownload = false) { - mtpFileLoader *l = loader; - loader = 0; - if (l) { - l->cancel(); - l->deleteLater(); - l->rpcInvalidate(); + QString already(bool check = false) const; + QByteArray data() const; + const FileLocation &location(bool check = false) const; + void setLocation(const FileLocation &loc); - notifyLayoutChanged(); - } - _location = FileLocation(); - if (!beforeDownload) { - openOnSave = 0; - openOnSaveMsgId = FullMsgId(); - } + bool saveToCache() const { + return false; } - void finish() { - if (loader->done()) { - _location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); - } - loader->deleteLater(); - loader->rpcInvalidate(); - loader = 0; + void performActionOnLoad(); - notifyLayoutChanged(); - } - - void notifyLayoutChanged() const; - - QString already(bool check = false); - const FileLocation &location(bool check = false); - - float64 progress() const { - return loader ? loader->currentProgress() : ((status == FileDownloadFailed || _location.name().isEmpty()) ? 0 : 1); - } + void forget(); VideoId id; uint64 access; @@ -845,14 +846,15 @@ struct VideoData { FileStatus status; int32 uploadOffset; - mtpTypeId fileType; - int32 openOnSave; - FullMsgId openOnSaveMsgId; - mtpFileLoader *loader; - private: FileLocation _location; + ActionOnLoad _actionOnLoad; + FullMsgId _actionOnLoadMsgId; + mutable mtpFileLoader *_loader; + + void notifyLayoutChanged() const; + }; class VideoLink : public ITextLink { @@ -867,6 +869,7 @@ public: private: VideoData *_video; + }; class VideoSaveLink : public VideoLink { @@ -886,6 +889,7 @@ public: VideoOpenLink(VideoData *video) : VideoLink(video) { } void onClick(Qt::MouseButton button) const; + }; class VideoCancelLink : public VideoLink { @@ -895,63 +899,36 @@ public: VideoCancelLink(VideoData *video) : VideoLink(video) { } void onClick(Qt::MouseButton button) const; + }; -struct AudioData { +class AudioData { +public: AudioData(const AudioId &id, const uint64 &access = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0); - void forget() { - } + void automaticLoad(const HistoryItem *item); // auto load voice message - bool prepareAutoLoader(const HistoryItem *item, bool force = false); - void save(const QString &toFile); + bool loaded(bool check = false) const; + bool loading() const; + bool displayLoading() const; + void save(const QString &toFile, ActionOnLoad action = ActionOnLoadNone, const FullMsgId &actionMsgId = FullMsgId(), LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, bool autoLoading = false); + void cancel(); + float64 progress() const; + int32 loadOffset() const; + bool uploading() const; - void cancel(bool beforeDownload = false) { - mtpFileLoader *l = loader; - loader = 0; - if (l) { - l->cancel(); - l->deleteLater(); - l->rpcInvalidate(); + QString already(bool check = false) const; + QByteArray data() const; + const FileLocation &location(bool check = false) const; + void setLocation(const FileLocation &loc); - notifyLayoutChanged(); - } - _location = FileLocation(); - if (!beforeDownload) { - openOnSave = 0; - openOnSaveMsgId = FullMsgId(); - } - } + bool saveToCache() const; - void finish() { - if (loader->done()) { - _location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); - data = loader->bytes(); - } - loader->deleteLater(); - loader->rpcInvalidate(); - loader = 0; + void performActionOnLoad(); - notifyLayoutChanged(); - } - void notifyLayoutChanged() const; - - QString already(bool check = false); - const FileLocation &location(bool check = false); - void setLocation(const FileLocation &loc) { - if (loc.check()) { - _location = loc; - } - } - bool loaded(bool check = false) { - return !data.isEmpty() || !already(check).isEmpty(); - } - bool loadingStarted() { - return loader && loader->started(); - } - - float64 progress() const { - return loader ? loader->currentProgress() : ((status == FileDownloadFailed || (_location.name().isEmpty() && data.isEmpty())) ? 0 : 1); + void forget(); + void setData(const QByteArray &data) { + _data = data; } AudioId id; @@ -965,14 +942,17 @@ struct AudioData { FileStatus status; int32 uploadOffset; - int32 openOnSave; - FullMsgId openOnSaveMsgId; - mtpFileLoader *loader; - QByteArray data; int32 md5[8]; private: FileLocation _location; + QByteArray _data; + + ActionOnLoad _actionOnLoad; + FullMsgId _actionOnLoadMsgId; + mutable mtpFileLoader *_loader; + + void notifyLayoutChanged() const; }; @@ -988,7 +968,9 @@ struct AudioMsgId { } AudioData *audio; FullMsgId msgId; + }; + inline bool operator<(const AudioMsgId &a, const AudioMsgId &b) { return quintptr(a.audio) < quintptr(b.audio) || (quintptr(a.audio) == quintptr(b.audio) && a.msgId < b.msgId); } @@ -1011,6 +993,7 @@ public: private: AudioData *_audio; + }; class AudioSaveLink : public AudioLink { @@ -1021,6 +1004,7 @@ public: } static void doSave(AudioData *audio, bool forceSavingAs = false); void onClick(Qt::MouseButton button) const; + }; class AudioOpenLink : public AudioLink { @@ -1030,6 +1014,7 @@ public: AudioOpenLink(AudioData *audio) : AudioLink(audio) { } void onClick(Qt::MouseButton button) const; + }; class AudioCancelLink : public AudioLink { @@ -1039,6 +1024,7 @@ public: AudioCancelLink(AudioData *audio) : AudioLink(audio) { } void onClick(Qt::MouseButton button) const; + }; enum DocumentType { @@ -1062,6 +1048,7 @@ struct StickerData : public DocumentAdditionalData { bool setInstalled() const; StorageImageLocation loc; // doc thumb location + }; struct SongData : public DocumentAdditionalData { @@ -1069,93 +1056,63 @@ struct SongData : public DocumentAdditionalData { } int32 duration; QString title, performer; + }; bool fileIsImage(const QString &name, const QString &mime); -struct DocumentData { +class DocumentData { +public: DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector &attributes = QVector(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0); void setattributes(const QVector &attributes); - void forget() { - thumb->forget(); - if (sticker()) sticker()->img->forget(); - replyPreview->forget(); - } - ImagePtr makeReplyPreview() { - if (replyPreview->isNull() && !thumb->isNull()) { - if (thumb->loaded()) { - int w = thumb->width(), h = thumb->height(); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - replyPreview = ImagePtr(w > h ? thumb->pix(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : thumb->pix(st::msgReplyBarSize.height()), "PNG"); - } else { - thumb->load(); - } - } - return replyPreview; - } + void automaticLoad(const HistoryItem *item); // auto load sticker or video - void save(const QString &toFile); + bool loaded(bool check = false) const; + bool loading() const; + bool displayLoading() const; + void save(const QString &toFile, ActionOnLoad action = ActionOnLoadNone, const FullMsgId &actionMsgId = FullMsgId(), LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, bool autoLoading = false); + void cancel(); + float64 progress() const; + int32 loadOffset() const; + bool uploading() const; - void cancel(bool beforeDownload = false) { - mtpFileLoader *l = loader; - loader = 0; - if (l) { - l->cancel(); - l->deleteLater(); - l->rpcInvalidate(); + QString already(bool check = false) const; + QByteArray data() const; + const FileLocation &location(bool check = false) const; + void setLocation(const FileLocation &loc); - notifyLayoutChanged(); - } - _location = FileLocation(); - if (!beforeDownload) { - openOnSave = 0; - openOnSaveMsgId = FullMsgId(); - } - } + bool saveToCache() const; - void finish() { - if (loader->done()) { - _location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); - data = loader->bytes(); - if (sticker() && !loader->imagePixmap().isNull()) { - sticker()->img = ImagePtr(data, loader->imageFormat(), loader->imagePixmap()); - } - } - loader->deleteLater(); - loader->rpcInvalidate(); - loader = 0; + void performActionOnLoad(); - notifyLayoutChanged(); - } - void notifyLayoutChanged() const; + void forget(); + ImagePtr makeReplyPreview(); - ~DocumentData() { - delete _additional; - } - - QString already(bool check = false); - const FileLocation &location(bool check = false); - void setLocation(const FileLocation &loc) { - if (loc.check()) { - _location = loc; - } - } - bool loaded(bool check = false) { - return !data.isEmpty() || !already(check).isEmpty(); - } - bool loadingStarted() { - return loader && loader->started(); - } - bool prepareAutoLoader(const HistoryItem *item, bool force = false); StickerData *sticker() { return (type == StickerDocument) ? static_cast(_additional) : 0; } + void checkSticker() { + StickerData *s = sticker(); + if (!s) return; + + automaticLoad(0); + if (s->img->isNull() && loaded()) { + if (_data.isEmpty()) { + const FileLocation &location(location(true)); + if (location.accessEnable()) { + s->img = ImagePtr(location.name()); + location.accessDisable(); + } + } else { + s->img = ImagePtr(_data); + } + } + } SongData *song() { return (type == SongDocument) ? static_cast(_additional) : 0; } - bool isAnimation() { + bool isAnimation() const { return (type == AnimatedDocument) || !mime.compare(qstr("image/gif"), Qt::CaseInsensitive); } bool isImage() const { @@ -1163,9 +1120,7 @@ struct DocumentData { } void recountIsImage(); - float64 progress() const { - return loader ? loader->currentProgress() : ((status == FileDownloadFailed || (_location.name().isEmpty() && data.isEmpty())) ? 0 : 1); - } + ~DocumentData(); DocumentId id; DocumentType type; @@ -1180,20 +1135,21 @@ struct DocumentData { FileStatus status; int32 uploadOffset; - int32 openOnSave; - FullMsgId openOnSaveMsgId; - mtpFileLoader *loader; - - QByteArray data; - DocumentAdditionalData *_additional; - int32 md5[8]; private: FileLocation _location; + QByteArray _data; + DocumentAdditionalData *_additional; bool _isImage; + ActionOnLoad _actionOnLoad; + FullMsgId _actionOnLoadMsgId; + mutable mtpFileLoader *_loader; + + void notifyLayoutChanged() const; + }; struct SongMsgId { @@ -1208,6 +1164,7 @@ struct SongMsgId { } DocumentData *song; FullMsgId msgId; + }; inline bool operator<(const SongMsgId &a, const SongMsgId &b) { return quintptr(a.song) < quintptr(b.song) || (quintptr(a.song) == quintptr(b.song) && a.msgId < b.msgId); @@ -1231,6 +1188,7 @@ public: private: DocumentData *_document; + }; class DocumentSaveLink : public DocumentLink { @@ -1241,6 +1199,7 @@ public: } static void doSave(DocumentData *document, bool forceSavingAs = false); void onClick(Qt::MouseButton button) const; + }; class DocumentOpenLink : public DocumentLink { @@ -1249,8 +1208,9 @@ class DocumentOpenLink : public DocumentLink { public: DocumentOpenLink(DocumentData *document) : DocumentLink(document) { } - static void doOpen(DocumentData *document, int32 openOnSave = 1); + static void doOpen(DocumentData *document, ActionOnLoad action = ActionOnLoadOpen); void onClick(Qt::MouseButton button) const; + }; class GifOpenLink : public DocumentOpenLink { @@ -1261,6 +1221,7 @@ public: } static void doOpen(DocumentData *document); void onClick(Qt::MouseButton button) const; + }; class DocumentCancelLink : public DocumentLink { @@ -1270,6 +1231,7 @@ public: DocumentCancelLink(DocumentData *document) : DocumentLink(document) { } void onClick(Qt::MouseButton button) const; + }; enum WebPageType { @@ -1300,6 +1262,7 @@ struct WebPageData { PhotoData *photo; DocumentData *doc; int32 pendingTill; + }; QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir = QDir()); diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 8f18d992f..5e533830c 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -38,6 +38,11 @@ typedef quint64 uint64; static const int32 ScrollMax = INT_MAX; +static uint64 SharedMemoryLocation0 = 0x00; +static uint64 SharedMemoryLocation1 = 0x01; +static uint64 SharedMemoryLocation2 = 0x02; +static uint64 SharedMemoryLocation3 = 0x03; + #ifdef Q_OS_WIN typedef float float32; typedef double float64;