diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 9695dc1d3..eb01491f9 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -582,6 +582,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org "lng_media_auto_gif" = "Automatic GIF download"; "lng_media_auto_private_chats" = "Private chats"; "lng_media_auto_groups" = "Groups and channels"; +"lng_media_auto_play" = "Autoplay"; "lng_emoji_category0" = "Frequently used"; "lng_emoji_category1" = "People"; @@ -596,6 +597,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org "lng_switch_stickers_gifs" = "GIFs & Stickers"; "lng_switch_emoji" = "Emoji"; +"lng_saved_gifs" = "Saved GIFs"; + "lng_box_remove" = "Remove"; "lng_custom_stickers" = "Custom stickers"; @@ -724,6 +727,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org "lng_context_delete_file" = "Delete File"; "lng_context_close_file" = "Close File"; "lng_context_copy_text" = "Copy Text"; +"lng_context_save_gif" = "Save GIF"; "lng_context_to_msg" = "Go To Message"; "lng_context_reply_msg" = "Reply"; "lng_context_forward_msg" = "Forward Message"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 7e31296dd..ad646bfed 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1865,6 +1865,8 @@ emojiObjectsActive: sprite(308px, 264px, 21px, 22px); emojiSymbolsOver: sprite(84px, 196px, 21px, 22px); emojiSymbolsActive: sprite(287px, 286px, 21px, 22px); stickersSettings: sprite(140px, 124px, 21px, 22px); +savedGifsOver: sprite(329px, 286px, 21px, 22px); +savedGifsActive: sprite(350px, 286px, 21px, 22px); emojiPanCategories: #f7f7f7; @@ -1989,6 +1991,11 @@ stickerPreviewDuration: 150; stickerPreviewBg: #FFFFFFB0; stickerPreviewMin: 0.1; +savedGifsLeft: 15px; +savedGifsSkip: 3px; +savedGifHeight: 96px; +savedGifMinWidth: 64px; + verifiedCheckProfile: sprite(285px, 235px, 18px, 18px); verifiedCheckProfilePos: point(7px, 6px); verifiedCheck: sprite(285px, 221px, 14px, 14px); @@ -2020,7 +2027,7 @@ botKbScroll: flatScroll(solidScroll) { width: 10px; } -minPhotoSize: 104px; +minPhotoSize: 100px; maxMediaSize: 420px; maxStickerSize: 256px; maxGifSize: 320px; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 8ce702b2a..46b95ce1c 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -915,5 +915,5 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs } ApiWrap::~ApiWrap() { - App::deinitMedia(false); + App::clearHistories(); } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 9c4ac09af..cf5372e4e 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -45,21 +45,14 @@ namespace { typedef QMap UpdatedPeers; UpdatedPeers updatedPeers; - typedef QHash PhotosData; PhotosData photosData; - - typedef QHash VideosData; VideosData videosData; - - typedef QHash AudiosData; AudiosData audiosData; + DocumentsData documentsData; typedef QHash ImageLinksData; ImageLinksData imageLinksData; - typedef QHash DocumentsData; - DocumentsData documentsData; - typedef QHash WebPagesData; WebPagesData webPagesData; @@ -918,11 +911,41 @@ namespace App { existing->setViewsCount(m.has_views() ? m.vviews.v : -1); - return !existing->detached(); + if (!existing->detached()) { + App::checkSavedGif(existing); + return true; + } + + return false; } return false; } + void addSavedGif(DocumentData *doc) { + SavedGifs &saved(cRefSavedGifs()); + int32 index = saved.indexOf(doc); + if (index) { + if (index > 0) saved.remove(index); + saved.push_front(doc); + if (saved.size() > cSavedGifsLimit()) saved.pop_back(); + Local::writeSavedGifs(); + + if (App::main()) emit App::main()->savedGifsUpdated(); + } + } + + void checkSavedGif(HistoryItem *item) { + if (!item->toHistoryForwarded() && item->out()) { + if (HistoryMedia *media = item->getMedia()) { + if (DocumentData *doc = media->getDocument()) { + if (doc->type == AnimatedDocument && doc->mime.toLower() == qstr("video/mp4")) { + addSavedGif(doc); + } + } + } + } + } + void feedMsgs(const QVector &msgs, NewMessageType type) { QMap msgsIds; for (int32 i = 0, l = msgs.size(); i < l; ++i) { @@ -1417,9 +1440,9 @@ namespace App { } PhotoData *photo(const PhotoId &photo) { - PhotosData::const_iterator i = photosData.constFind(photo); - if (i == photosData.cend()) { - i = photosData.insert(photo, new PhotoData(photo)); + PhotosData::const_iterator i = ::photosData.constFind(photo); + if (i == ::photosData.cend()) { + i = ::photosData.insert(photo, new PhotoData(photo)); } return i.value(); } @@ -1427,9 +1450,9 @@ namespace App { PhotoData *photoSet(const PhotoId &photo, PhotoData *convert, const uint64 &access, int32 date, const ImagePtr &thumb, const ImagePtr &medium, const ImagePtr &full) { if (convert) { if (convert->id != photo) { - PhotosData::iterator i = photosData.find(convert->id); - if (i != photosData.cend() && i.value() == convert) { - photosData.erase(i); + PhotosData::iterator i = ::photosData.find(convert->id); + if (i != ::photosData.cend() && i.value() == convert) { + ::photosData.erase(i); } convert->id = photo; delete convert->uploadingData; @@ -1443,16 +1466,16 @@ namespace App { convert->full = full; } } - PhotosData::const_iterator i = photosData.constFind(photo); + PhotosData::const_iterator i = ::photosData.constFind(photo); PhotoData *result; LastPhotosMap::iterator inLastIter = lastPhotosMap.end(); - if (i == photosData.cend()) { + if (i == ::photosData.cend()) { if (convert) { result = convert; } else { result = new PhotoData(photo, access, date, thumb, medium, full); } - photosData.insert(photo, result); + ::photosData.insert(photo, result); } else { result = i.value(); if (result != convert && !result->date && date) { @@ -1479,9 +1502,9 @@ namespace App { } VideoData *video(const VideoId &video) { - VideosData::const_iterator i = videosData.constFind(video); - if (i == videosData.cend()) { - i = videosData.insert(video, new VideoData(video)); + VideosData::const_iterator i = ::videosData.constFind(video); + if (i == ::videosData.cend()) { + i = ::videosData.insert(video, new VideoData(video)); } return i.value(); } @@ -1489,9 +1512,9 @@ namespace App { VideoData *videoSet(const VideoId &video, VideoData *convert, const uint64 &access, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) { if (convert) { if (convert->id != video) { - VideosData::iterator i = videosData.find(convert->id); - if (i != videosData.cend() && i.value() == convert) { - videosData.erase(i); + VideosData::iterator i = ::videosData.find(convert->id); + if (i != ::videosData.cend() && i.value() == convert) { + ::videosData.erase(i); } convert->id = video; convert->status = FileReady; @@ -1507,15 +1530,15 @@ namespace App { convert->size = size; } } - VideosData::const_iterator i = videosData.constFind(video); + VideosData::const_iterator i = ::videosData.constFind(video); VideoData *result; - if (i == videosData.cend()) { + if (i == ::videosData.cend()) { if (convert) { result = convert; } else { result = new VideoData(video, access, date, duration, w, h, thumb, dc, size); } - videosData.insert(video, result); + ::videosData.insert(video, result); } else { result = i.value(); if (result != convert && !result->date && date) { @@ -1533,9 +1556,9 @@ namespace App { } AudioData *audio(const AudioId &audio) { - AudiosData::const_iterator i = audiosData.constFind(audio); - if (i == audiosData.cend()) { - i = audiosData.insert(audio, new AudioData(audio)); + AudiosData::const_iterator i = ::audiosData.constFind(audio); + if (i == ::audiosData.cend()) { + i = ::audiosData.insert(audio, new AudioData(audio)); } return i.value(); } @@ -1543,9 +1566,9 @@ namespace App { AudioData *audioSet(const AudioId &audio, AudioData *convert, const uint64 &access, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) { if (convert) { if (convert->id != audio) { - AudiosData::iterator i = audiosData.find(convert->id); - if (i != audiosData.cend() && i.value() == convert) { - audiosData.erase(i); + AudiosData::iterator i = ::audiosData.find(convert->id); + if (i != ::audiosData.cend() && i.value() == convert) { + ::audiosData.erase(i); } convert->id = audio; convert->status = FileReady; @@ -1559,15 +1582,15 @@ namespace App { convert->size = size; } } - AudiosData::const_iterator i = audiosData.constFind(audio); + AudiosData::const_iterator i = ::audiosData.constFind(audio); AudioData *result; - if (i == audiosData.cend()) { + if (i == ::audiosData.cend()) { if (convert) { result = convert; } else { result = new AudioData(audio, access, date, mime, duration, dc, size); } - audiosData.insert(audio, result); + ::audiosData.insert(audio, result); } else { result = i.value(); if (result != convert && !result->date && date) { @@ -1583,9 +1606,9 @@ namespace App { } DocumentData *document(const DocumentId &document) { - DocumentsData::const_iterator i = documentsData.constFind(document); - if (i == documentsData.cend()) { - i = documentsData.insert(document, new DocumentData(document)); + DocumentsData::const_iterator i = ::documentsData.constFind(document); + if (i == ::documentsData.cend()) { + i = ::documentsData.insert(document, new DocumentData(document)); } return i.value(); } @@ -1594,9 +1617,9 @@ namespace App { bool sentSticker = false; if (convert) { if (convert->id != document) { - DocumentsData::iterator i = documentsData.find(convert->id); - if (i != documentsData.cend() && i.value() == convert) { - documentsData.erase(i); + DocumentsData::iterator i = ::documentsData.find(convert->id); + if (i != ::documentsData.cend() && i.value() == convert) { + ::documentsData.erase(i); } convert->id = document; convert->status = FileReady; @@ -1636,9 +1659,9 @@ namespace App { Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), loc); } } - DocumentsData::const_iterator i = documentsData.constFind(document); + DocumentsData::const_iterator i = ::documentsData.constFind(document); DocumentData *result; - if (i == documentsData.cend()) { + if (i == ::documentsData.cend()) { if (convert) { result = convert; } else { @@ -1646,7 +1669,7 @@ namespace App { result->recountIsImage(); if (result->sticker()) result->sticker()->loc = thumbLocation; } - documentsData.insert(document, result); + ::documentsData.insert(document, result); } else { result = i.value(); if (result != convert) { @@ -1776,16 +1799,16 @@ namespace App { void forgetMedia() { lastPhotos.clear(); lastPhotosMap.clear(); - for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) { + for (PhotosData::const_iterator i = ::photosData.cbegin(), e = ::photosData.cend(); i != e; ++i) { i.value()->forget(); } - for (VideosData::const_iterator i = videosData.cbegin(), e = videosData.cend(); i != e; ++i) { + for (VideosData::const_iterator i = ::videosData.cbegin(), e = ::videosData.cend(); i != e; ++i) { i.value()->forget(); } - for (AudiosData::const_iterator i = audiosData.cbegin(), e = audiosData.cend(); i != e; ++i) { + for (AudiosData::const_iterator i = ::audiosData.cbegin(), e = ::audiosData.cend(); i != e; ++i) { i.value()->forget(); } - for (DocumentsData::const_iterator i = documentsData.cbegin(), e = documentsData.cend(); i != e; ++i) { + for (DocumentsData::const_iterator i = ::documentsData.cbegin(), e = ::documentsData.cend(); i != e; ++i) { i.value()->forget(); } for (ImageLinksData::const_iterator i = imageLinksData.cbegin(), e = imageLinksData.cend(); i != e; ++i) { @@ -1933,32 +1956,33 @@ namespace App { delete *i; } peersData.clear(); - for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) { + for (PhotosData::const_iterator i = ::photosData.cbegin(), e = ::photosData.cend(); i != e; ++i) { delete *i; } - photosData.clear(); - for (VideosData::const_iterator i = videosData.cbegin(), e = videosData.cend(); i != e; ++i) { + ::photosData.clear(); + for (VideosData::const_iterator i = ::videosData.cbegin(), e = ::videosData.cend(); i != e; ++i) { delete *i; } - videosData.clear(); - for (AudiosData::const_iterator i = audiosData.cbegin(), e = audiosData.cend(); i != e; ++i) { + ::videosData.clear(); + for (AudiosData::const_iterator i = ::audiosData.cbegin(), e = ::audiosData.cend(); i != e; ++i) { delete *i; } - audiosData.clear(); - for (DocumentsData::const_iterator i = documentsData.cbegin(), e = documentsData.cend(); i != e; ++i) { + ::audiosData.clear(); + for (DocumentsData::const_iterator i = ::documentsData.cbegin(), e = ::documentsData.cend(); i != e; ++i) { delete *i; } - documentsData.clear(); + ::documentsData.clear(); for (WebPagesData::const_iterator i = webPagesData.cbegin(), e = webPagesData.cend(); i != e; ++i) { delete *i; } webPagesData.clear(); if (api()) api()->clearWebPageRequests(); cSetRecentStickers(RecentStickerPack()); - cSetStickersHash(0); cSetStickerSets(StickerSets()); cSetStickerSetsOrder(StickerSetsOrder()); cSetLastStickersUpdate(0); + cSetSavedGifs(SavedGifs()); + cSetLastSavedGifsUpdate(0); cSetReportSpamStatuses(ReportSpamStatuses()); ::photoItems.clear(); ::videoItems.clear(); @@ -2057,7 +2081,6 @@ namespace App { } void initMedia() { - deinitMedia(false); audioInit(); if (!::monofont) { @@ -2119,48 +2142,47 @@ namespace App { prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInBgSelected, &st::msgInShadowSelected); prepareCorners(MessageOutCorners, st::msgRadius, st::msgOutBg, &st::msgOutShadow); prepareCorners(MessageOutSelectedCorners, st::msgRadius, st::msgOutBgSelected, &st::msgOutShadowSelected); - } - void deinitMedia(bool completely) { + void clearHistories() { textlnkOver(TextLinkPtr()); textlnkDown(TextLinkPtr()); histories().clear(); - - if (completely) { - audioFinish(); - - delete ::sprite; - ::sprite = 0; - delete ::emoji; - ::emoji = 0; - delete ::emojiLarge; - ::emojiLarge = 0; - for (int32 j = 0; j < 4; ++j) { - for (int32 i = 0; i < RoundCornersCount; ++i) { - delete ::corners[i].p[j]; ::corners[i].p[j] = 0; - } - delete ::cornersMask[j]; ::cornersMask[j] = 0; - } - for (CornersMap::const_iterator i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) { - for (int32 j = 0; j < 4; ++j) { - delete i->p[j]; - } - } - ::cornersMap.clear(); - mainEmojiMap.clear(); - otherEmojiMap.clear(); - - clearAllImages(); - } else { - clearStorageImages(); - cSetServerBackgrounds(WallPapers()); - } + + clearStorageImages(); + cSetServerBackgrounds(WallPapers()); serviceImageCacheSize = imageCacheSize(); } + void deinitMedia() { + audioFinish(); + + delete ::sprite; + ::sprite = 0; + delete ::emoji; + ::emoji = 0; + delete ::emojiLarge; + ::emojiLarge = 0; + for (int32 j = 0; j < 4; ++j) { + for (int32 i = 0; i < RoundCornersCount; ++i) { + delete ::corners[i].p[j]; ::corners[i].p[j] = 0; + } + delete ::cornersMask[j]; ::cornersMask[j] = 0; + } + for (CornersMap::const_iterator i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) { + for (int32 j = 0; j < 4; ++j) { + delete i->p[j]; + } + } + ::cornersMap.clear(); + mainEmojiMap.clear(); + otherEmojiMap.clear(); + + clearAllImages(); + } + void hoveredItem(HistoryItem *item) { ::hoveredItem = item; } @@ -2362,6 +2384,10 @@ namespace App { return ::photoItems; } + const PhotosData &photosData() { + return ::photosData; + } + void regVideoItem(VideoData *data, HistoryItem *item) { ::videoItems[data].insert(item, NullType()); } @@ -2374,6 +2400,10 @@ namespace App { return ::videoItems; } + const VideosData &videosData() { + return ::videosData; + } + void regAudioItem(AudioData *data, HistoryItem *item) { ::audioItems[data].insert(item, NullType()); } @@ -2386,6 +2416,10 @@ namespace App { return ::audioItems; } + const AudiosData &audiosData() { + return ::audiosData; + } + void regDocumentItem(DocumentData *data, HistoryItem *item) { ::documentItems[data].insert(item, NullType()); } @@ -2398,6 +2432,10 @@ namespace App { return ::documentItems; } + const DocumentsData &documentsData() { + return ::documentsData; + } + void regWebPageItem(WebPageData *data, HistoryItem *item) { ::webPageItems[data].insert(item, NullType()); } @@ -2436,9 +2474,10 @@ namespace App { void stopGifItems() { if (!::gifItems.isEmpty()) { - if (HistoryItem *playing = ::gifItems.begin().value()) { - if (playing->getMedia()) { - playing->getMedia()->stopInline(playing); + GifItems gifs = ::gifItems; + for (GifItems::const_iterator i = gifs.cbegin(), e = gifs.cend(); i != e; ++i) { + if (HistoryMedia *media = i.value()->getMedia()) { + media->stopInline(i.value()); } } } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 5bebaf9d0..02146bd58 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -35,13 +35,19 @@ class FileUploader; #include "layout.h" typedef QMap HistoryItemsMap; -typedef QMap PhotoItems; -typedef QMap VideoItems; -typedef QMap AudioItems; -typedef QMap DocumentItems; -typedef QMap WebPageItems; -typedef QMap SharedContactItems; -typedef QMap GifItems; +typedef QHash PhotoItems; +typedef QHash VideoItems; +typedef QHash AudioItems; +typedef QHash DocumentItems; +typedef QHash WebPageItems; +typedef QHash SharedContactItems; +typedef QHash GifItems; + +typedef QHash PhotosData; +typedef QHash VideosData; +typedef QHash AudiosData; +typedef QHash DocumentsData; + struct ReplyMarkup { ReplyMarkup(int32 flags = 0) : flags(flags) { } @@ -79,6 +85,8 @@ namespace App { void feedChatAdmins(const MTPDupdateChatAdmins &d, bool emitPeerUpdated = true); void feedParticipantAdmin(const MTPDupdateChatParticipantAdmin &d, bool emitPeerUpdated = true); bool checkEntitiesAndViewsUpdate(const MTPDmessage &m); // returns true if item found and it is not detached + void addSavedGif(DocumentData *doc); + void checkSavedGif(HistoryItem *item); void feedMsgs(const QVector &msgs, NewMessageType type); void feedMsgs(const MTPVector &msgs, NewMessageType type); void feedInboxRead(const PeerId &peer, MsgId upTo); @@ -184,8 +192,10 @@ namespace App { const QPixmap &emojiLarge(); const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight); + void clearHistories(); + void initMedia(); - void deinitMedia(bool completely = true); + void deinitMedia(); void playSound(); void checkImageCacheSize(); @@ -202,18 +212,22 @@ namespace App { void regPhotoItem(PhotoData *data, HistoryItem *item); void unregPhotoItem(PhotoData *data, HistoryItem *item); const PhotoItems &photoItems(); + const PhotosData &photosData(); void regVideoItem(VideoData *data, HistoryItem *item); void unregVideoItem(VideoData *data, HistoryItem *item); const VideoItems &videoItems(); + const VideosData &videosData(); void regAudioItem(AudioData *data, HistoryItem *item); void unregAudioItem(AudioData*data, HistoryItem *item); const AudioItems &audioItems(); + const AudiosData &audiosData(); void regDocumentItem(DocumentData *data, HistoryItem *item); void unregDocumentItem(DocumentData *data, HistoryItem *item); const DocumentItems &documentItems(); + const DocumentsData &documentsData(); void regWebPageItem(WebPageData *data, HistoryItem *item); void unregWebPageItem(WebPageData *data, HistoryItem *item); @@ -276,25 +290,4 @@ namespace App { }; -inline int32 stickersCountHash(bool checkOfficial = false) { - uint32 acc = 0; - bool foundOfficial = false, foundBad = false;; - const StickerSets &sets(cStickerSets()); - const StickerSetsOrder &order(cStickerSetsOrder()); - for (StickerSetsOrder::const_iterator i = order.cbegin(), e = order.cend(); i != e; ++i) { - StickerSets::const_iterator j = sets.constFind(*i); - if (j != sets.cend()) { - if (j->id == 0) { - foundBad = true; - } else if (j->flags & MTPDstickerSet::flag_official) { - foundOfficial = true; - } - if (!(j->flags & MTPDstickerSet::flag_disabled)) { - acc = (acc * 20261) + j->hash; - } - } - } - return (!checkOfficial || (!foundBad && foundOfficial)) ? int32(acc & 0x7FFFFFFF) : 0; -} - #include "facades.h" diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 79a869454..7bf606124 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index a0a3d4511..50dfed9c3 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index 78fb0dd03..22e0d7321 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -226,11 +226,12 @@ AutoDownloadBox::AutoDownloadBox() : AbstractBox(st::boxWidth) , _audioGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadAudio() & dbiadNoGroups)) , _gifPrivate(this, lang(lng_media_auto_private_chats), !(cAutoDownloadGif() & dbiadNoPrivate)) , _gifGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadGif() & dbiadNoGroups)) +, _gifPlay(this, lang(lng_media_auto_play), cAutoPlayGif()) , _sectionHeight(st::boxTitleHeight + 2 * (st::defaultCheckbox.height + st::setLittleSkip)) , _save(this, lang(lng_connection_save), st::defaultBoxButton) , _cancel(this, lang(lng_cancel), st::cancelBoxButton) { - setMaxHeight(3 * _sectionHeight + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); + setMaxHeight(3 * _sectionHeight + st::setLittleSkip + _gifPlay.height() + st::setLittleSkip + st::boxButtonPadding.top() + _save.height() + st::boxButtonPadding.bottom()); connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); @@ -245,6 +246,7 @@ void AutoDownloadBox::hideAll() { _audioGroups.hide(); _gifPrivate.hide(); _gifGroups.hide(); + _gifPlay.hide(); _save.hide(); _cancel.hide(); @@ -257,6 +259,7 @@ void AutoDownloadBox::showAll() { _audioGroups.show(); _gifPrivate.show(); _gifGroups.show(); + _gifPlay.show(); _save.show(); _cancel.show(); @@ -282,15 +285,60 @@ void AutoDownloadBox::resizeEvent(QResizeEvent *e) { _gifPrivate.moveToLeft(st::boxTitlePosition.x(), 2 * _sectionHeight + st::boxTitleHeight + st::setLittleSkip); _gifGroups.moveToLeft(st::boxTitlePosition.x(), _gifPrivate.y() + _gifPrivate.height() + st::setLittleSkip); + _gifPlay.moveToLeft(st::boxTitlePosition.x(), _gifGroups.y() + _gifGroups.height() + st::setLittleSkip); _save.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save.height()); _cancel.moveToRight(st::boxButtonPadding.right() + _save.width() + st::boxButtonPadding.left(), _save.y()); } void AutoDownloadBox::onSave() { - cSetAutoDownloadPhoto((_photoPrivate.checked() ? 0 : dbiadNoPrivate) | (_photoGroups.checked() ? 0 : dbiadNoGroups)); - cSetAutoDownloadAudio((_audioPrivate.checked() ? 0 : dbiadNoPrivate) | (_audioGroups.checked() ? 0 : dbiadNoGroups)); - cSetAutoDownloadGif((_gifPrivate.checked() ? 0 : dbiadNoPrivate) | (_gifGroups.checked() ? 0 : dbiadNoGroups)); - Local::writeUserSettings(); + bool changed = false; + int32 autoDownloadPhoto = (_photoPrivate.checked() ? 0 : dbiadNoPrivate) | (_photoGroups.checked() ? 0 : dbiadNoGroups); + if (cAutoDownloadPhoto() != autoDownloadPhoto) { + bool enabledPrivate = ((cAutoDownloadPhoto() & dbiadNoPrivate) && !(autoDownloadPhoto & dbiadNoPrivate)); + bool enabledGroups = ((cAutoDownloadPhoto() & dbiadNoGroups) && !(autoDownloadPhoto & dbiadNoGroups)); + cSetAutoDownloadPhoto(autoDownloadPhoto); + if (enabledPrivate || enabledGroups) { + const PhotosData &data(App::photosData()); + for (PhotosData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) { + i.value()->automaticLoadSettingsChanged(); + } + } + changed = true; + } + int32 autoDownloadAudio = (_audioPrivate.checked() ? 0 : dbiadNoPrivate) | (_audioGroups.checked() ? 0 : dbiadNoGroups); + if (cAutoDownloadAudio() != autoDownloadAudio) { + bool enabledPrivate = ((cAutoDownloadAudio() & dbiadNoPrivate) && !(autoDownloadAudio & dbiadNoPrivate)); + bool enabledGroups = ((cAutoDownloadAudio() & dbiadNoGroups) && !(autoDownloadAudio & dbiadNoGroups)); + cSetAutoDownloadAudio(autoDownloadAudio); + if (enabledPrivate || enabledGroups) { + const AudiosData &data(App::audiosData()); + for (AudiosData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) { + i.value()->automaticLoadSettingsChanged(); + } + } + changed = true; + } + int32 autoDownloadGif = (_gifPrivate.checked() ? 0 : dbiadNoPrivate) | (_gifGroups.checked() ? 0 : dbiadNoGroups); + if (cAutoDownloadGif() != autoDownloadGif) { + bool enabledPrivate = ((cAutoDownloadGif() & dbiadNoPrivate) && !(autoDownloadGif & dbiadNoPrivate)); + bool enabledGroups = ((cAutoDownloadGif() & dbiadNoGroups) && !(autoDownloadGif & dbiadNoGroups)); + cSetAutoDownloadGif(autoDownloadGif); + if (enabledPrivate || enabledGroups) { + const DocumentsData &data(App::documentsData()); + for (DocumentsData::const_iterator i = data.cbegin(), e = data.cend(); i != e; ++i) { + i.value()->automaticLoadSettingsChanged(); + } + } + changed = true; + } + if (cAutoPlayGif() != _gifPlay.checked()) { + cSetAutoPlayGif(_gifPlay.checked()); + if (!cAutoPlayGif()) { + App::stopGifItems(); + } + changed = true; + } + if (changed) Local::writeUserSettings(); onClose(); } diff --git a/Telegram/SourceFiles/boxes/connectionbox.h b/Telegram/SourceFiles/boxes/connectionbox.h index bd54f178d..79a43f378 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.h +++ b/Telegram/SourceFiles/boxes/connectionbox.h @@ -77,7 +77,7 @@ private: Checkbox _photoPrivate, _photoGroups; Checkbox _audioPrivate, _audioGroups; - Checkbox _gifPrivate, _gifGroups; + Checkbox _gifPrivate, _gifGroups, _gifPlay; int32 _sectionHeight; diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index d3f235ff8..b47ee602f 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -112,7 +112,6 @@ void StickerSetInner::installDone(const MTPBool &result) { sets.erase(custom); } } - cSetStickersHash(stickersCountHash()); Local::writeStickers(); emit installed(_setId); Ui::hideLayer(); @@ -892,7 +891,6 @@ void StickersBox::onSave() { } } - cSetStickersHash(stickersCountHash()); Local::writeStickers(); if (writeRecent) Local::writeUserSettings(); emit App::main()->stickersUpdated(); diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index cdfc578e9..11212bdf5 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -130,6 +130,7 @@ enum { EmojiPanRowsPerPage = 6, StickerPanPerRow = 5, StickerPanRowsPerPage = 4, + SavedGifsMaxPerRow = 4, StickersUpdateTimeout = 3600000, // update not more than once in an hour SearchPeopleLimit = 5, diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 07e408455..c424fcdb2 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "window.h" #include "apiwrap.h" +#include "mainwidget.h" #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" @@ -732,9 +733,6 @@ EmojiPanInner::EmojiPanInner() : TWidget() _hovers[i] = QVector(_counts[i], 0); } - _saveConfigTimer.setSingleShot(true); - connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig())); - _showPickerTimer.setSingleShot(true); connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker())); connect(&_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr))); @@ -937,15 +935,11 @@ void EmojiPanInner::selectEmoji(EmojiPtr emoji) { qSwap(*i, *(i - 1)); } } - _saveConfigTimer.start(SaveRecentEmojisTimeout); + emit saveConfigDelayed(SaveRecentEmojisTimeout); emit selected(emoji); } -void EmojiPanInner::onSaveConfig() { - Local::writeUserSettings(); -} - void EmojiPanInner::onShowPicker() { int tab = (_pickerSel / MatrixRowShift), sel = _pickerSel % MatrixRowShift; if (tab < emojiTabCount && sel < _emojis[tab].size() && _emojis[tab][sel]->color) { @@ -1219,6 +1213,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { StickerPanInner::StickerPanInner() : TWidget() , _a_selected(animation(this, &StickerPanInner::step_selected)) , _top(0) +, _showingGifs(cShowingSavedGifs()) , _selected(-1) , _pressedSel(-1) , _settings(this, lang(lng_stickers_you_have)) @@ -1234,8 +1229,6 @@ StickerPanInner::StickerPanInner() : TWidget() _previewTimer.setSingleShot(true); connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); - - refreshStickers(); } void StickerPanInner::setMaxHeight(int32 h) { @@ -1253,11 +1246,15 @@ void StickerPanInner::setScrollTop(int top) { int StickerPanInner::countHeight() { int result = 0, minLastH = _maxHeight - st::rbEmoji.height - st::stickerPanPadding; - for (int i = 0; i < _sets.size(); ++i) { - int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); - int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); - if (i == _sets.size() - 1 && h < minLastH) h = minLastH; - result += h; + if (_showingGifs) { + result = st::emojiPanHeader + _gifRows.count() * (st::savedGifHeight + st::savedGifsSkip) - st::savedGifsSkip; + } else { + for (int i = 0; i < _sets.size(); ++i) { + int cnt = _sets.at(i).pack.size(), rows = (cnt / StickerPanPerRow) + ((cnt % StickerPanPerRow) ? 1 : 0); + int h = st::emojiPanHeader + rows * st::stickerPanSize.height(); + if (i == _sets.size() - 1 && h < minLastH) h = minLastH; + result += h; + } } return qMax(minLastH, result) + st::stickerPanPadding; } @@ -1285,8 +1282,39 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { if (r != rect()) { p.setClipRect(r); } - p.fillRect(r, st::white->b); + p.fillRect(r, st::white); + if (_showingGifs) { + paintSavedGifs(p, r); + } else { + paintStickers(p, r); + } +} + +void StickerPanInner::paintSavedGifs(Painter &p, const QRect &r) { + uint64 ms = getms(); + + int32 fromrow = floorclamp(r.y() - st::emojiPanHeader, st::savedGifHeight + st::savedGifsSkip, 0, _gifRows.size()); + int32 torow = ceilclamp(r.y() + r.height() - st::emojiPanHeader - st::savedGifsSkip, st::savedGifHeight + st::savedGifsSkip, 0, _gifRows.size()); + int32 fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width()); + for (int32 row = fromrow; row < torow; ++row) { + const GifRow &gifRow(_gifRows.at(row)); + int32 left = st::savedGifsLeft, top = st::emojiPanHeader + row * (st::savedGifHeight + st::savedGifsSkip); + for (int32 col = 0, cols = gifRow.size(); col < cols; ++col) { + if (left >= tox) break; + + int32 w = gifRow.at(col)->width(); + if (left + w > fromx) { + p.translate(left, top); + gifRow.at(col)->paint(p, _previewShown, ms); + p.translate(-left, -top); + } + left += w + st::savedGifsSkip; + } + } +} + +void StickerPanInner::paintStickers(Painter &p, const QRect &r) { int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, StickerPanPerRow); if (rtl()) { @@ -1382,6 +1410,35 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { } if (_selected < 0 || _selected != pressed) return; + if (_showingGifs) { + int32 row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; + bool del = (col >= SavedGifsMaxPerRow); + if (del) col -= SavedGifsMaxPerRow; + if (row < _gifRows.size() && col < _gifRows.at(row).size()) { + DocumentData *doc = _gifRows.at(row).at(col)->document(); + if (del) { + int32 index = cSavedGifs().indexOf(doc); + if (index >= 0) { + cRefSavedGifs().remove(index); + Local::writeSavedGifs(); + if (App::main()) emit App::main()->savedGifsUpdated(); + + MTP::send(MTPmessages_SaveGif(MTP_inputDocument(MTP_long(doc->id), MTP_long(doc->access)), MTP_bool(true))); + } else { + refreshSavedGifs(); + } + } else { + if (doc->loaded()) { + emit selected(doc); + } else if (doc->loading()) { + doc->cancel(); + } else { + DocumentOpenLink::doOpen(doc, ActionOnLoadNone); + } + } + } + return; + } if (_selected >= MatrixRowShift * _sets.size()) { return; } @@ -1416,7 +1473,7 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { } } if (refresh) { - refreshRecent(); + refreshRecentStickers(); updateSelected(); update(); } @@ -1448,6 +1505,20 @@ void StickerPanInner::enterFromChildEvent(QEvent *e) { void StickerPanInner::clearSelection(bool fast) { _lastMousePos = mapToGlobal(QPoint(-10, -10)); if (fast) { + if (_showingGifs) { + if (_selected >= 0) { + int32 srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift; + bool sdel = (scol >= SavedGifsMaxPerRow); + if (sdel) scol -= SavedGifsMaxPerRow; + if (srow < _gifRows.size() && scol < _gifRows.at(srow).size()) { + _gifRows.at(srow).at(scol)->notify_over(false); + if (sdel) _gifRows.at(srow).at(scol)->notify_deleteOver(false); + } + setCursor(style::cur_default); + } + _selected = _pressedSel = -1; + return; + } for (Animations::const_iterator i = _animations.cbegin(); i != _animations.cend(); ++i) { int index = qAbs(i.key()) - 1, tab = (index / MatrixRowShift), sel = index % MatrixRowShift; _sets[tab].hovers[sel] = 0; @@ -1476,28 +1547,156 @@ void StickerPanInner::clearSelection(bool fast) { } } +void StickerPanInner::hideFinish() { + clearSavedGifs(); + for (GifLayouts::const_iterator i = _gifLayouts.cbegin(), e = _gifLayouts.cend(); i != e; ++i) { + i.value()->document()->forget(); + } +} + void StickerPanInner::refreshStickers() { clearSelection(true); const StickerSets &sets(cStickerSets()); _sets.clear(); _sets.reserve(sets.size() + 1); - refreshRecent(false); + refreshRecentStickers(false); for (StickerSetsOrder::const_iterator i = cStickerSetsOrder().cbegin(), e = cStickerSetsOrder().cend(); i != e; ++i) { appendSet(*i); } - int32 h = countHeight(); - if (h != height()) resize(width(), h); + if (!_showingGifs) { + int32 h = countHeight(); + if (h != height()) resize(width(), h); + + _settings.setVisible(_sets.isEmpty()); + } else { + _settings.hide(); + } - _settings.setVisible(_sets.isEmpty()); emit refreshIcons(); updateSelected(); } +void StickerPanInner::refreshSavedGifs() { + clearSelection(true); + + clearSavedGifs(); + if (_showingGifs) { + const SavedGifs &saved(cSavedGifs()); + if (saved.isEmpty()) { + showStickerSet(RecentStickerSetId); + return; + } else { + _gifRows.reserve(saved.size()); + GifRow row; + row.reserve(SavedGifsMaxPerRow); + int32 maxWidth = width() - st::savedGifsLeft, sumWidth = 0, widths[SavedGifsMaxPerRow] = { 0 }; + for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { + DocumentData *doc = *i; + int32 w = doc->dimensions.width(), h = doc->dimensions.height(); + if ((w <= 0 || h <= 0) && !doc->thumb->isNull()) { + w = doc->thumb->width(); + h = doc->thumb->height(); + } + if (w <= 0 || h <= 0) continue; + + w = w * st::savedGifHeight / h; + widths[row.size()] = w; + + w = qMax(w, int32(st::savedGifMinWidth)); + sumWidth += w; + row.push_back(layoutPrepare(doc, (_gifRows.size() * MatrixRowShift) + row.size(), w)); + + if (row.size() >= SavedGifsMaxPerRow || sumWidth >= maxWidth - (row.size() - 1) * st::savedGifsSkip) { + _gifRows.push_back(layoutGifRow(row, widths, sumWidth)); + row.clear(); + row.reserve(SavedGifsMaxPerRow); + sumWidth = 0; + memset(widths, 0, sizeof(widths)); + } + } + if (!row.isEmpty()) { + _gifRows.push_back(row); + } + } + deleteUnusedLayouts(); + + int32 h = countHeight(); + if (h != height()) resize(width(), h); + + update(); + } + + emit refreshIcons(); + + updateSelected(); +} + +void StickerPanInner::clearSavedGifs() { + for (GifRows::const_iterator i = _gifRows.cbegin(), e = _gifRows.cend(); i != e; ++i) { + for (GifRow::const_iterator j = i->cbegin(), end = i->cend(); j != end; ++j) { + (*j)->setPosition(-1, 0); + } + } + _gifRows.clear(); +} + +void StickerPanInner::deleteUnusedLayouts() { + if (_gifRows.isEmpty()) { // delete all + for (GifLayouts::const_iterator i = _gifLayouts.cbegin(), e = _gifLayouts.cend(); i != e; ++i) { + delete i.value(); + } + _gifLayouts.clear(); + } else { + for (GifLayouts::iterator i = _gifLayouts.begin(); i != _gifLayouts.cend();) { + if (i.value()->position() < 0) { + delete i.value(); + i = _gifLayouts.erase(i); + } else { + ++i; + } + } + } +} + +LayoutSavedGif *StickerPanInner::layoutPrepare(DocumentData *doc, int32 position, int32 width) { + GifLayouts::const_iterator i = _gifLayouts.constFind(doc); + if (i == _gifLayouts.cend()) { + i = _gifLayouts.insert(doc, new LayoutSavedGif(doc)); + } + i.value()->setPosition(position, width); + return i.value(); +} + +const StickerPanInner::GifRow &StickerPanInner::layoutGifRow(const GifRow &row, int32 *widths, int32 sumWidth) { + int32 count = row.size(); + t_assert(count <= SavedGifsMaxPerRow); + + int32 availw = width() - st::savedGifsLeft - st::savedGifsSkip * (count - 1); + if (sumWidth != availw) { + for (int32 i = 0; i < count; ++i) { + int32 w = widths[i] * availw / sumWidth; + int32 actualw = qMax(w, int32(st::savedGifMinWidth)); + row.at(i)->setWidth(actualw); + + availw -= actualw; + sumWidth -= qMax(widths[i], int32(st::savedGifMinWidth)); + } + } + return row; +} + void StickerPanInner::preloadImages() { + if (_showingGifs) { + for (int32 row = 0, rows = _gifRows.size(); row < rows; ++row) { + for (int32 col = 0, cols = _gifRows.at(row).size(); col < cols; ++col) { + _gifRows.at(row).at(col)->preload(); + } + } + } uint64 ms = getms(); for (int32 i = 0, l = _sets.size(), k = 0; i < l; ++i) { for (int32 j = 0, n = _sets.at(i).pack.size(); j < n; ++j) { @@ -1518,6 +1717,8 @@ void StickerPanInner::preloadImages() { } uint64 StickerPanInner::currentSet(int yOffset) const { + if (_showingGifs) return NoneStickerSetId; + int y, ytill = 0; for (int i = 0, l = _sets.size(); i < l; ++i) { int cnt = _sets.at(i).pack.size(); @@ -1530,6 +1731,33 @@ uint64 StickerPanInner::currentSet(int yOffset) const { return _sets.isEmpty() ? RecentStickerSetId : _sets.back().id; } +void StickerPanInner::ui_repaintSavedGif(const LayoutSavedGif *layout) { + if (!_showingGifs) return; + + int32 position = layout->position(); + int32 row = position / MatrixRowShift, col = position % MatrixRowShift; + t_assert((row < _gifRows.size()) && (col < _gifRows.at(row).size())); + + const GifRow &gifRow(_gifRows.at(row)); + int32 left = st::savedGifsLeft, top = st::emojiPanHeader + row * (st::savedGifHeight + st::savedGifsSkip); + for (int32 i = 0; i < col; ++i) left += gifRow.at(i)->width() + st::savedGifsSkip; + + rtlupdate(left, top, gifRow.at(col)->width(), st::savedGifHeight); +} + +bool StickerPanInner::ui_isSavedGifVisible(const LayoutSavedGif *layout) { + int32 position = layout->position(); + int32 row = position / MatrixRowShift, col = position % MatrixRowShift; + t_assert((row < _gifRows.size()) && (col < _gifRows.at(row).size())); + + int32 top = st::emojiPanHeader + row * (st::savedGifHeight + st::savedGifsSkip); + return (top < _top + _maxHeight) && (top + st::savedGifHeight > _top); +} + +bool StickerPanInner::ui_isGifBeingChosen() { + return _showingGifs; +} + void StickerPanInner::appendSet(uint64 setId) { const StickerSets &sets(cStickerSets()); StickerSets::const_iterator it = sets.constFind(setId); @@ -1543,7 +1771,15 @@ void StickerPanInner::appendSet(uint64 setId) { _sets.push_back(DisplayedSet(it->id, it->flags, it->title, pack.size() + 1, pack)); } -void StickerPanInner::refreshRecent(bool performResize) { +void StickerPanInner::refreshRecent() { + if (_showingGifs) { + refreshSavedGifs(); + } else { + refreshRecentStickers(); + } +} + +void StickerPanInner::refreshRecentStickers(bool performResize) { _custom.clear(); clearSelection(true); StickerSets::const_iterator customIt = cStickerSets().constFind(CustomStickerSetId); @@ -1580,7 +1816,7 @@ void StickerPanInner::refreshRecent(bool performResize) { } } - if (performResize) { + if (performResize && !_showingGifs) { int32 h = countHeight(); if (h != height()) { resize(width(), h); @@ -1591,14 +1827,15 @@ void StickerPanInner::refreshRecent(bool performResize) { } } -void StickerPanInner::fillIcons(QVector &icons) { +void StickerPanInner::fillIcons(QList &icons) { icons.clear(); - if (_sets.isEmpty()) return; + icons.reserve(_sets.size() + 1); + if (!cSavedGifs().isEmpty()) icons.push_back(StickerIcon(NoneStickerSetId)); - icons.reserve(_sets.size()); + if (_sets.isEmpty()) return; int32 i = 0; if (_sets.at(0).id == RecentStickerSetId) ++i; - if (i > 0) icons.push_back(StickerIcon()); + if (i > 0) icons.push_back(StickerIcon(RecentStickerSetId)); for (int32 l = _sets.size(); i < l; ++i) { DocumentData *s = _sets.at(i).pack.at(0); int32 availw = st::rbEmoji.width - 2 * st::stickerIconPadding, availh = st::rbEmoji.height - 2 * st::stickerIconPadding; @@ -1622,6 +1859,13 @@ void StickerPanInner::fillPanels(QVector &panels) { panels.at(i)->deleteLater(); } panels.clear(); + + if (_showingGifs) { + panels.push_back(new EmojiPanel(parentWidget(), lang(lng_saved_gifs), NoneStickerSetId, true, 0)); + panels.back()->show(); + return; + } + if (_sets.isEmpty()) return; int y = 0; @@ -1637,8 +1881,9 @@ void StickerPanInner::fillPanels(QVector &panels) { } } - void StickerPanInner::refreshPanels(QVector &panels) { + if (_showingGifs) return; + if (panels.size() != _sets.size()) return fillPanels(panels); int32 y = 0; @@ -1656,6 +1901,64 @@ void StickerPanInner::updateSelected() { int32 selIndex = -1; QPoint p(mapFromGlobal(_lastMousePos)); + if (_showingGifs) { + int sx = (rtl() ? width() - p.x() : p.x()) - st::savedGifsLeft, sy = p.y() - st::emojiPanHeader; + int32 row = sy / int32(st::savedGifHeight + st::savedGifsSkip), col = 0, sel = -1; + bool del = false; + if (sx >= 0 && row >= 0 && row < _gifRows.size() && sy < (row + 1) * st::savedGifHeight) { + const GifRow &gifRow(_gifRows.at(row)); + for (int32 left = 0, cols = gifRow.size(); col < cols; ++col) { + int32 width = gifRow.at(col)->width(); + if (sx >= left && sx < left + width) { + del = (sx >= left + width - st::stickerPanDelete.pxWidth()) && (sy < row * st::savedGifHeight + st::stickerPanDelete.pxHeight()); + break; + } + left += width + st::savedGifsSkip; + } + if (col < gifRow.size()) { + sel = row * MatrixRowShift + col + (del ? SavedGifsMaxPerRow : 0); + } else { + row = col = -1; + } + } else { + row = col = -1; + } + if (_selected != sel) { + int32 srow = (_selected >= 0) ? (_selected / MatrixRowShift) : -1; + int32 scol = (_selected >= 0) ? (_selected % MatrixRowShift) : -1; + bool sdel = (scol >= SavedGifsMaxPerRow); + if (sdel) scol -= SavedGifsMaxPerRow; + if (srow != row || scol != col) { + if (srow >= 0 && srow < _gifRows.size()) { + if (scol >= 0 && scol < _gifRows.at(srow).size()) { + _gifRows.at(srow).at(scol)->notify_over(false); + if (sdel) _gifRows.at(srow).at(scol)->notify_deleteOver(false); + } + } + if (row >= 0 && row < _gifRows.size()) { + if (col >= 0 && col < _gifRows.at(row).size()) { + _gifRows.at(row).at(col)->notify_over(true); + if (del) _gifRows.at(row).at(col)->notify_deleteOver(true); + } + } + } else if (sdel != del) { + if (sdel) _gifRows.at(srow).at(scol)->notify_deleteOver(false); + if (del) _gifRows.at(row).at(col)->notify_deleteOver(true); + } + if ((_selected >= 0 && sel < 0) || (_selected < 0 && sel >= 0)) { + setCursor(sel >= 0 ? style::cur_pointer : style::cur_default); + } + _selected = sel; + if (_pressedSel >= 0 && _selected >= 0 && _pressedSel != _selected) { + _pressedSel = _selected; + if (row >= 0 && col >= 0) { + Ui::showStickerPreview(_gifRows.at(row).at(col)->document()); + } + } + } + return; + } + int y, ytill = 0, sx = (rtl() ? width() - p.x() : p.x()) - st::stickerPanPadding; for (int c = 0, l = _sets.size(); c < l; ++c) { const DisplayedSet &set(_sets.at(c)); @@ -1742,7 +2045,15 @@ void StickerPanInner::onSettings() { } void StickerPanInner::onPreview() { - if (_pressedSel >= 0 && _pressedSel < MatrixRowShift * _sets.size()) { + if (_pressedSel < 0) return; + if (_showingGifs) { + int32 row = _pressedSel / MatrixRowShift, col = _pressedSel % MatrixRowShift; + if (col >= SavedGifsMaxPerRow) col -= SavedGifsMaxPerRow; + if (row < _gifRows.size() && col < _gifRows.at(row).size() && _gifRows.at(row).at(col)->document()->loaded()) { + Ui::showStickerPreview(_gifRows.at(row).at(col)->document()); + _previewShown = true; + } + } else if (_pressedSel < MatrixRowShift * _sets.size()) { int tab = (_pressedSel / MatrixRowShift), sel = _pressedSel % MatrixRowShift; if (sel < _sets.at(tab).pack.size()) { Ui::showStickerPreview(_sets.at(tab).pack.at(sel)); @@ -1765,13 +2076,40 @@ void StickerPanInner::step_selected(uint64 ms, bool timer) { } toUpdate += stickerRect(tab, sel); } - if (timer)rtlupdate(toUpdate.boundingRect()); + if (timer) rtlupdate(toUpdate.boundingRect()); if (_animations.isEmpty()) _a_selected.stop(); } void StickerPanInner::showStickerSet(uint64 setId) { clearSelection(true); + if (setId == NoneStickerSetId) { + if (_gifRows.isEmpty() && !cSavedGifs().isEmpty()) { + refreshSavedGifs(); + } + bool wasNotShowingGifs = !_showingGifs; + if (wasNotShowingGifs) { + _showingGifs = true; + cSetShowingSavedGifs(true); + emit saveConfigDelayed(SaveRecentEmojisTimeout); + } + refreshSavedGifs(); + emit scrollToY(0); + emit scrollUpdated(); + return; + } + + if (_showingGifs) { + _showingGifs = false; + cSetShowingSavedGifs(false); + emit saveConfigDelayed(SaveRecentEmojisTimeout); + + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); + + refreshRecentStickers(true); + emit refreshIcons(); + } + int32 y = 0; for (int c = 0; c < _sets.size(); ++c) { if (_sets.at(c).id == setId) break; @@ -1780,14 +2118,19 @@ void StickerPanInner::showStickerSet(uint64 setId) { } emit scrollToY(y); + emit scrollUpdated(); _lastMousePos = QCursor::pos(); update(); } -EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent), -_wantedY(wantedY), _setId(setId), _special(special), _deleteVisible(false), _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // NoneStickerSetId if in emoji +EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY) : TWidget(parent) +, _wantedY(wantedY) +, _setId(setId) +, _special(special) +, _deleteVisible(false) +, _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // NoneStickerSetId if in emoji resize(st::emojiPanWidth, st::emojiPanHeader); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); @@ -1815,7 +2158,7 @@ void EmojiPanel::updateText() { availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft; } } else { - QString switchText = lang((_setId != NoneStickerSetId) ? lng_switch_emoji : lng_switch_stickers); + QString switchText = lang((_setId != NoneStickerSetId) ? lng_switch_emoji : (cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs)); availw -= st::emojiSwitchSkip + st::emojiPanHeaderFont->width(switchText); } _text = st::emojiPanHeaderFont->elided(_fullText, availw); @@ -1847,11 +2190,17 @@ void EmojiPanel::paintEvent(QPaintEvent *e) { p.drawTextLeft(st::emojiPanHeaderLeft, st::emojiPanHeaderTop, width(), _text); } -EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent), -_toStickers(toStickers), _text(lang(_toStickers ? lng_switch_stickers : lng_switch_emoji)), -_textWidth(st::emojiPanHeaderFont->width(_text)) { - int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); +EmojiSwitchButton::EmojiSwitchButton(QWidget *parent, bool toStickers) : Button(parent) +, _toStickers(toStickers) { setCursor(style::cur_pointer); + updateText(); +} + +void EmojiSwitchButton::updateText() { + _text = lang(_toStickers ? (cSavedGifs().isEmpty() ? lng_switch_stickers : lng_switch_stickers_gifs) : lng_switch_emoji); + _textWidth = st::emojiPanHeaderFont->width(_text); + + int32 w = st::emojiSwitchSkip + _textWidth + (st::emojiSwitchSkip - st::emojiSwitchImgSkip); resize(w, st::emojiPanHeader); } @@ -1947,6 +2296,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) connect(&e_inner, SIGNAL(disableScroll(bool)), &e_scroll, SLOT(disableScroll(bool))); connect(&s_inner, SIGNAL(scrollToY(int)), &s_scroll, SLOT(scrollToY(int))); + connect(&s_inner, SIGNAL(scrollUpdated()), this, SLOT(onScroll())); connect(&e_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); connect(&s_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); @@ -1964,6 +2314,11 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent) connect(&e_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); connect(&s_inner, SIGNAL(needRefreshPanels()), this, SLOT(onRefreshPanels())); + _saveConfigTimer.setSingleShot(true); + connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig())); + connect(&e_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); + connect(&s_inner, SIGNAL(saveConfigDelayed(int32)), this, SLOT(onSaveConfigDelayed(int32))); + if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); } @@ -2020,6 +2375,14 @@ void EmojiPan::onWndActiveChanged() { } } +void EmojiPan::onSaveConfig() { + Local::writeUserSettings(); +} + +void EmojiPan::onSaveConfigDelayed(int32 delay) { + _saveConfigTimer.start(delay); +} + void EmojiPan::paintEvent(QPaintEvent *e) { Painter p(this); @@ -2040,24 +2403,20 @@ void EmojiPan::paintEvent(QPaintEvent *e) { p.drawSpriteLeft(_iconsLeft + 7 * st::rbEmoji.width + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::stickersSettings); if (!_icons.isEmpty()) { - int32 x = _iconsLeft, i = 0, selxrel = _iconSelX.current(), selx = x + selxrel - _iconsX.current(); - if (!_icons.at(i).sticker) { - if (selxrel > 0) { - if (_iconHovers.at(i) < 1) { - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::rbEmojiRecent.imageRect); - } - if (_iconHovers.at(i) > 0) { - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::rbEmojiRecent.overImageRect); - } + int32 x = _iconsLeft, i = 0, selxrel = _iconsLeft + _iconSelX.current(), selx = selxrel - _iconsX.current(); + for (int32 l = _icons.size(); i < l && !_icons.at(i).sticker; ++i) { + bool gifs = (_icons.at(i).setId == NoneStickerSetId); + if (selxrel != x) { + p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsOver : st::rbEmojiRecent.imageRect); } - if (selxrel < st::rbEmoji.width) { - p.setOpacity(1 - (selxrel / st::rbEmoji.width)); - p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), st::rbEmojiRecent.chkImageRect); + if (selxrel < x + st::rbEmoji.width && selxrel > x - st::rbEmoji.width) { + p.setOpacity(1 - (qAbs(selxrel - x) / st::rbEmoji.width)); + p.drawSpriteLeft(x + st::rbEmojiRecent.imagePos.x(), _iconsTop + st::rbEmojiRecent.imagePos.y(), width(), gifs ? st::savedGifsActive : st::rbEmojiRecent.chkImageRect); p.setOpacity(1); } x += st::rbEmoji.width; - ++i; } + int32 skip = i; QRect clip(x, _iconsTop, _iconsLeft + 7 * st::rbEmoji.width - x, st::rbEmoji.height); if (rtl()) clip.moveLeft(width() - x - clip.width()); @@ -2065,28 +2424,23 @@ void EmojiPan::paintEvent(QPaintEvent *e) { i += _iconsX.current() / int(st::rbEmoji.width); x -= _iconsX.current() % int(st::rbEmoji.width); - for (int32 l = qMin(_icons.size(), i + 7 + (_icons.at(0).sticker ? 1 : 0)); i < l; ++i) { + for (int32 l = qMin(_icons.size(), i + 8 - skip); i < l; ++i) { const StickerIcon &s(_icons.at(i)); s.sticker->thumb->load(); QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh)); - //if (_iconSel == i) { - // p.setOpacity(1); - //} else { - // p.setOpacity(1. * _iconHovers.at(i) + st::stickerIconOpacity * (1 - _iconHovers.at(i))); - //} + p.drawPixmapLeft(x + (st::rbEmoji.width - s.pixw) / 2, _iconsTop + (st::rbEmoji.height - s.pixh) / 2, width(), pix); x += st::rbEmoji.width; - p.setOpacity(1); } if (rtl()) selx = width() - selx - st::rbEmoji.width; - p.setOpacity(_icons.at(0).sticker ? 1. : qMax(1., selx / st::rbEmoji.width)); - p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor->b); + p.setOpacity(skip ? qMax(1., selx / (skip * st::rbEmoji.width)) : 1.); + p.fillRect(selx, _iconsTop + st::rbEmoji.height - st::stickerIconPadding, st::rbEmoji.width, st::stickerIconSel, st::stickerIconSelColor); float64 o_left = snap(float64(_iconsX.current()) / st::stickerIconLeft.pxWidth(), 0., 1.); if (o_left > 0) { p.setOpacity(o_left); - p.drawSpriteLeft(QRect(_iconsLeft + (_icons.at(0).sticker ? 0 : st::rbEmoji.width), _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft); + p.drawSpriteLeft(QRect(_iconsLeft + skip * st::rbEmoji.width, _iconsTop, st::stickerIconLeft.pxWidth(), st::rbEmoji.height), width(), st::stickerIconLeft); } float64 o_right = snap(float64(_iconsMax - _iconsX.current()) / st::stickerIconRight.pxWidth(), 0., 1.); if (o_right > 0) { @@ -2181,7 +2535,12 @@ void EmojiPan::mouseMoveEvent(QMouseEvent *e) { _iconsMousePos = e ? e->globalPos() : QCursor::pos(); updateSelected(); - if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= (_icons.at(0).sticker ? 0 : 1)) { + int32 skip = 0; + for (int32 i = 0, l = _icons.size(); i < l; ++i) { + if (_icons.at(i).sticker) break; + ++skip; + } + if (!_iconsDragging && !_icons.isEmpty() && _iconDown >= skip) { if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) { _iconsDragging = true; } @@ -2226,24 +2585,31 @@ void EmojiPan::mouseReleaseEvent(QMouseEvent *e) { bool EmojiPan::event(QEvent *e) { if (e->type() == QEvent::TouchBegin) { - int a = 0; - } else if (e->type() == QEvent::Wheel && !_icons.isEmpty() && _iconOver >= (_icons.at(0).sticker ? 0 : 1) && _iconOver < _icons.size() && _iconDown < 0) { - QWheelEvent *ev = static_cast(e); - bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal); - bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical); - if (hor) _horizontal = true; - int32 newX = _iconsX.current(); - if (/*_horizontal && */hor) { - newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax); - } else if (/*!_horizontal && */ver) { - newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax); + + } else if (e->type() == QEvent::Wheel) { + int32 skip = 0; + for (int32 i = 0, l = _icons.size(); i < l; ++i) { + if (_icons.at(i).sticker) break; + ++skip; } - if (newX != _iconsX.current()) { - _iconsX = anim::ivalue(newX, newX); - _iconsStartAnim = 0; - if (_iconAnimations.isEmpty()) _a_icons.stop(); - updateSelected(); - updateIcons(); + if (!_icons.isEmpty() && _iconOver >= skip && _iconOver < _icons.size() && _iconDown < 0) { + QWheelEvent *ev = static_cast(e); + bool hor = (ev->angleDelta().x() != 0 || ev->orientation() == Qt::Horizontal); + bool ver = (ev->angleDelta().y() != 0 || ev->orientation() == Qt::Vertical); + if (hor) _horizontal = true; + int32 newX = _iconsX.current(); + if (/*_horizontal && */hor) { + newX = snap(newX - (rtl() ? -1 : 1) * (ev->pixelDelta().x() ? ev->pixelDelta().x() : ev->angleDelta().x()), 0, _iconsMax); + } else if (/*!_horizontal && */ver) { + newX = snap(newX - (ev->pixelDelta().y() ? ev->pixelDelta().y() : ev->angleDelta().y()), 0, _iconsMax); + } + if (newX != _iconsX.current()) { + _iconsX = anim::ivalue(newX, newX); + _iconsStartAnim = 0; + if (_iconAnimations.isEmpty()) _a_icons.stop(); + updateSelected(); + updateIcons(); + } } } return TWidget::event(e); @@ -2266,6 +2632,15 @@ void EmojiPan::refreshStickers() { } } +void EmojiPan::refreshSavedGifs() { + e_switch.updateText(); + e_switch.moveToRight(0, 0, st::emojiPanWidth); + s_inner.refreshSavedGifs(); + if (!_stickersShown) { + s_inner.preloadImages(); + } +} + void EmojiPan::onRefreshIcons() { _iconOver = -1; _iconHovers.clear(); @@ -2284,6 +2659,7 @@ void EmojiPan::onRefreshIcons() { } updatePanelsPositions(s_panels, s_scroll.scrollTop()); updateSelected(); + if (_stickersShown) validateSelectedIcon(); updateIcons(); } @@ -2316,16 +2692,18 @@ void EmojiPan::updateSelected() { newOver = _icons.size(); } else if (!_icons.isEmpty()) { if (y >= _iconsTop && y < _iconsTop + st::rbEmoji.height && x >= 0 && x < 7 * st::rbEmoji.width && x < _icons.size() * st::rbEmoji.width) { - if (!_icons.at(0).sticker) { + int32 skip = 0; + for (int32 i = 0, l = _icons.size(); i < l; ++i) { if (x < st::rbEmoji.width) { - newOver = 0; - } else { - x -= st::rbEmoji.width; + newOver = i; + break; } + x -= st::rbEmoji.width; + ++skip; } if (newOver < 0) { x += _iconsX.current(); - newOver = qFloor(x / st::rbEmoji.width) + (_icons.at(0).sticker ? 0 : 1); + newOver = qFloor(x / st::rbEmoji.width) + skip; } } } @@ -2459,6 +2837,7 @@ void EmojiPan::hideStart() { void EmojiPan::hideFinish() { hide(); e_inner.hideFinish(); + s_inner.hideFinish(); _cache = _toCache = _fromCache = QPixmap(); _a_slide.stop(); _horizontal = false; @@ -2478,6 +2857,8 @@ void EmojiPan::hideFinish() { _a_icons.stop(); _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); _iconAnimations.clear(); + + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); } void EmojiPan::showStart() { @@ -2543,6 +2924,26 @@ void EmojiPan::stickersInstalled(uint64 setId) { showStart(); } +void EmojiPan::ui_repaintSavedGif(const LayoutSavedGif *layout) { + if (_stickersShown && !isHidden()) { + s_inner.ui_repaintSavedGif(layout); + } +} + +bool EmojiPan::ui_isSavedGifVisible(const LayoutSavedGif *layout) { + if (_stickersShown && !isHidden()) { + return s_inner.ui_isSavedGifVisible(layout); + } + return false; +} + +bool EmojiPan::ui_isGifBeingChosen() { + if (_stickersShown && !isHidden()) { + return s_inner.ui_isGifBeingChosen(); + } + return false; +} + void EmojiPan::showAll() { if (_stickersShown) { s_scroll.show(); @@ -2636,38 +3037,60 @@ void EmojiPan::onScroll() { st = s_scroll.scrollTop(); if (_stickersShown) { updatePanelsPositions(s_panels, st); - - uint64 setId = s_inner.currentSet(st); - int32 newSel = 0; - for (int32 i = 0, l = _icons.size(); i < l; ++i) { - if (_icons.at(i).setId == setId) { - newSel = i; - break; - } - } - if (newSel != _iconSel) { - _iconSel = newSel; - _iconSelX.start(newSel * st::rbEmoji.width); - _iconsX.start(snap((2 * newSel - 7 - ((_icons.isEmpty() || _icons.at(0).sticker) ? 0 : 1)) * int(st::rbEmoji.width) / 2, 0, _iconsMax)); - _iconsStartAnim = getms(); - _a_icons.start(); - updateSelected(); - updateIcons(); - } + validateSelectedIcon(true); } s_inner.setScrollTop(st); } +void EmojiPan::validateSelectedIcon(bool animated) { + uint64 setId = s_inner.currentSet(s_scroll.scrollTop()); + int32 newSel = 0; + for (int32 i = 0, l = _icons.size(); i < l; ++i) { + if (_icons.at(i).setId == setId) { + newSel = i; + break; + } + } + if (newSel != _iconSel) { + _iconSel = newSel; + int32 skip = 0; + for (int32 i = 0, l = _icons.size(); i < l; ++i) { + if (_icons.at(i).sticker) break; + ++skip; + } + if (animated) { + _iconSelX.start(newSel * st::rbEmoji.width); + } else { + _iconSelX = anim::ivalue(newSel * st::rbEmoji.width, newSel * st::rbEmoji.width); + } + _iconsX.start(snap((2 * newSel - 7 - skip) * int(st::rbEmoji.width) / 2, 0, _iconsMax)); + _iconsStartAnim = getms(); + _a_icons.start(); + updateSelected(); + updateIcons(); + } +} + void EmojiPan::onSwitch() { QPixmap cache = _cache; _fromCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); _stickersShown = !_stickersShown; + if (!_stickersShown) { + Notify::clipStopperHidden(ClipStopperSavedGifsPanel); + } + if (cShowingSavedGifs() && cSavedGifs().isEmpty()) { + s_inner.showStickerSet(DefaultStickerSetId); + } else if (!cShowingSavedGifs() && !cSavedGifs().isEmpty() && cStickerSets().isEmpty()) { + s_inner.showStickerSet(NoneStickerSetId); + } _iconOver = -1; _iconHovers = _icons.isEmpty() ? QVector() : QVector(_icons.size(), 0); _iconAnimations.clear(); _a_icons.stop(); + validateSelectedIcon(); + _cache = QPixmap(); showAll(); _toCache = myGrab(this, rect().marginsRemoved(st::dropdownDef.padding)); @@ -2721,7 +3144,6 @@ void EmojiPan::onRemoveSetSure() { cRefStickerSets().erase(it); int32 removeIndex = cStickerSetsOrder().indexOf(_removingSetId); if (removeIndex >= 0) cRefStickerSetsOrder().removeAt(removeIndex); - cSetStickersHash(stickersCountHash()); refreshStickers(); Local::writeStickers(); if (writeRecent) Local::writeUserSettings(); @@ -3337,5 +3759,16 @@ bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) { return QWidget::eventFilter(obj, e); } +void MentionsDropdown::ui_repaintSavedGif(const LayoutSavedGif *layout) { +} + +bool MentionsDropdown::ui_isSavedGifVisible(const LayoutSavedGif *layout) { + return false; +} + +bool MentionsDropdown::ui_isGifBeingChosen() { + return false; +} + MentionsDropdown::~MentionsDropdown() { } diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index 4b5ef4741..fc69d249d 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -258,7 +258,6 @@ public: public slots: void updateSelected(); - void onSaveConfig(); void onShowPicker(); void onPickerHidden(); @@ -276,6 +275,7 @@ signals: void disableScroll(bool dis); void needRefreshPanels(); + void saveConfigDelayed(int32 delay); private: @@ -300,14 +300,12 @@ private: int32 _selected, _pressedSel, _pickerSel; QPoint _lastMousePos; - QTimer _saveConfigTimer; - EmojiColorPicker _picker; QTimer _showPickerTimer; }; struct StickerIcon { - StickerIcon() : setId(RecentStickerSetId), sticker(0), pixw(0), pixh(0) { + StickerIcon(uint64 setId) : setId(setId), sticker(0), pixw(0), pixh(0) { } StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) { } @@ -335,14 +333,17 @@ public: void step_selected(uint64 ms, bool timer); + void hideFinish(); void showStickerSet(uint64 setId); void clearSelection(bool fast = false); void refreshStickers(); - void refreshRecent(bool resize = true); + void refreshRecentStickers(bool resize = true); + void refreshSavedGifs(); + void refreshRecent(); - void fillIcons(QVector &icons); + void fillIcons(QList &icons); void fillPanels(QVector &panels); void refreshPanels(QVector &panels); @@ -351,6 +352,15 @@ public: uint64 currentSet(int yOffset) const; + void ui_repaintSavedGif(const LayoutSavedGif *layout); + bool ui_isSavedGifVisible(const LayoutSavedGif *layout); + bool ui_isGifBeingChosen(); + + ~StickerPanInner() { + clearSavedGifs(); + deleteUnusedLayouts(); + } + public slots: void updateSelected(); @@ -367,11 +377,17 @@ signals: void switchToEmoji(); void scrollToY(int y); + void scrollUpdated(); void disableScroll(bool dis); void needRefreshPanels(); + void saveConfigDelayed(int32 delay); + private: + void paintSavedGifs(Painter &p, const QRect &r); + void paintStickers(Painter &p, const QRect &r); + int32 _maxHeight; void appendSet(uint64 setId); @@ -398,6 +414,19 @@ private: QList _sets; QList _custom; + bool _showingGifs; + + typedef QList GifRow; + typedef QList GifRows; + GifRows _gifRows; + void clearSavedGifs(); + void deleteUnusedLayouts(); + + typedef QMap GifLayouts; + GifLayouts _gifLayouts; + LayoutSavedGif *layoutPrepare(DocumentData *doc, int32 position, int32 width); + const GifRow &layoutGifRow(const GifRow &row, int32 *widths, int32 sumWidth); + int32 _selected, _pressedSel; QPoint _lastMousePos; @@ -452,6 +481,7 @@ public: EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji void paintEvent(QPaintEvent *e); + void updateText(); protected: @@ -504,9 +534,14 @@ public: ).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); } + void ui_repaintSavedGif(const LayoutSavedGif *layout); + bool ui_isSavedGifVisible(const LayoutSavedGif *layout); + bool ui_isGifBeingChosen(); + public slots: void refreshStickers(); + void refreshSavedGifs(); void hideStart(); void hideFinish(); @@ -525,6 +560,9 @@ public slots: void onRefreshIcons(); void onRefreshPanels(); + void onSaveConfig(); + void onSaveConfigDelayed(int32 delay); + signals: void emojiSelected(EmojiPtr emoji); @@ -533,6 +571,8 @@ signals: private: + void validateSelectedIcon(bool animated = false); + int32 _maxHeight; bool _horizontal; @@ -561,7 +601,7 @@ private: BoxShadow _shadow; FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols; - QVector _icons; + QList _icons; QVector _iconHovers; int32 _iconOver, _iconSel, _iconDown; bool _iconsDragging; @@ -591,6 +631,8 @@ private: uint64 _removingSetId; + QTimer _saveConfigTimer; + }; typedef QList MentionRows; @@ -679,6 +721,10 @@ public: return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); } + void ui_repaintSavedGif(const LayoutSavedGif *layout); + bool ui_isSavedGifVisible(const LayoutSavedGif *layout); + bool ui_isGifBeingChosen(); + ~MentionsDropdown(); signals: diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index cc37c0c81..511dbadb0 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -102,20 +102,36 @@ namespace Ui { return false; } - void clipRedraw(ClipReader *reader) { + bool isGifBeingChosen() { + if (MainWidget *m = App::main()) return m->ui_isGifBeingChosen(); + return false; + } + + void clipRepaint(ClipReader *reader) { const GifItems &items(App::gifItems()); GifItems::const_iterator it = items.constFind(reader); if (it != items.cend()) { if (reader->currentDisplayed()) { return; } - Ui::redrawHistoryItem(it.value()); + Ui::repaintHistoryItem(it.value()); } - if (Window *w = App::wnd()) w->ui_clipRedraw(reader); + if (Window *w = App::wnd()) w->ui_clipRepaint(reader); } - void redrawHistoryItem(const HistoryItem *item) { - if (MainWidget *m = App::main()) m->ui_redrawHistoryItem(item); + void repaintHistoryItem(const HistoryItem *item) { + if (!item) return; + if (MainWidget *m = App::main()) m->ui_repaintHistoryItem(item); + } + + void repaintSavedGif(const LayoutSavedGif *layout) { + if (!layout) return; + if (MainWidget *m = App::main()) m->ui_repaintSavedGif(layout); + } + + bool isSavedGifVisible(const LayoutSavedGif *layout) { + if (MainWidget *m = App::main()) return m->ui_isSavedGifVisible(layout); + return false; } void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) { @@ -148,8 +164,8 @@ namespace Notify { if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer); } - void mediaViewHidden() { - if (MainWidget *m = App::main()) m->notify_mediaViewHidden(); + void clipStopperHidden(ClipStopperType type) { + if (MainWidget *m = App::main()) m->notify_clipStopperHidden(type); } void clipReinit(ClipReader *reader) { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 00f0355cc..ca254fb26 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -46,10 +46,13 @@ namespace Ui { // openssl doesn't allow me to use UI :( void hideLayer(bool fast = false); bool isLayerShown(); bool isMediaViewShown(); + bool isGifBeingChosen(); - void clipRedraw(ClipReader *reader); + void clipRepaint(ClipReader *reader); - void redrawHistoryItem(const HistoryItem *item); + void repaintHistoryItem(const HistoryItem *item); + void repaintSavedGif(const LayoutSavedGif *layout); + bool isSavedGifVisible(const LayoutSavedGif *reader); void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false); inline void showPeerHistory(const PeerData *peer, MsgId msgId, bool back = false) { @@ -68,6 +71,11 @@ namespace Ui { // openssl doesn't allow me to use UI :( }; +enum ClipStopperType { + ClipStopperMediaview, + ClipStopperSavedGifsPanel, +}; + namespace Notify { void userIsBotChanged(UserData *user); @@ -76,7 +84,7 @@ namespace Notify { void migrateUpdated(PeerData *peer); - void mediaViewHidden(); + void clipStopperHidden(ClipStopperType type); void clipReinit(ClipReader *reader); diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 1295534ad..59830a2af 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -118,34 +118,104 @@ void Animation::stop() { _manager->stop(this); } -void AnimationManager::clipReinit(ClipReader *reader) { +AnimationManager::AnimationManager() : _timer(this), _iterating(false) { + _timer.setSingleShot(false); + connect(&_timer, SIGNAL(timeout()), this, SLOT(timeout())); +} + +void AnimationManager::start(Animation *obj) { + if (_iterating) { + _starting.insert(obj, NullType()); + if (!_stopping.isEmpty()) { + _stopping.remove(obj); + } + } else { + if (_objects.isEmpty()) { + _timer.start(AnimationTimerDelta); + } + _objects.insert(obj, NullType()); + } +} + +void AnimationManager::stop(Animation *obj) { + if (_iterating) { + _stopping.insert(obj, NullType()); + if (!_starting.isEmpty()) { + _starting.insert(obj, NullType()); + } + } else { + AnimatingObjects::iterator i = _objects.find(obj); + if (i != _objects.cend()) { + _objects.erase(i); + if (_objects.isEmpty()) { + _timer.stop(); + } + } + } +} + +void AnimationManager::timeout() { + _iterating = true; + uint64 ms = getms(); + for (AnimatingObjects::const_iterator i = _objects.begin(), e = _objects.end(); i != e; ++i) { + i.key()->step(ms, true); + } + _iterating = false; + + if (!_starting.isEmpty()) { + for (AnimatingObjects::iterator i = _starting.begin(), e = _starting.end(); i != e; ++i) { + _objects.insert(i.key(), NullType()); + } + _starting.clear(); + } + if (!_stopping.isEmpty()) { + for (AnimatingObjects::iterator i = _stopping.begin(), e = _stopping.end(); i != e; ++i) { + _objects.remove(i.key()); + } + _stopping.clear(); + } + if (!_objects.size()) { + _timer.stop(); + } +} + +void AnimationManager::clipReinit(ClipReader *reader, qint32 threadIndex) { + ClipReader::callback(reader, threadIndex, ClipReaderReinit); Notify::clipReinit(reader); } -void AnimationManager::clipRedraw(ClipReader *reader) { - Ui::clipRedraw(reader); +void AnimationManager::clipRepaint(ClipReader *reader, qint32 threadIndex) { + ClipReader::callback(reader, threadIndex, ClipReaderRepaint); + Ui::clipRepaint(reader); } -QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, QImage &cache, bool smooth) { +QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, QImage &cache, bool hasAlpha) { bool badSize = (original.width() != request.framew) || (original.height() != request.frameh); bool needOuter = (request.outerw != request.framew) || (request.outerh != request.frameh); - if (badSize || needOuter || request.rounded) { + if (badSize || needOuter || hasAlpha || request.rounded) { int32 factor(request.factor); bool fill = false; if (cache.width() != request.outerw || cache.height() != request.outerh) { cache = QImage(request.outerw, request.outerh, QImage::Format_ARGB32_Premultiplied); - if (request.framew < request.outerw || request.frameh < request.outerh || original.hasAlphaChannel()) { + if (request.framew < request.outerw || request.frameh < request.outerh || hasAlpha) { fill = true; } cache.setDevicePixelRatio(factor); } { Painter p(&cache); - if (fill) p.fillRect(0, 0, cache.width() / factor, cache.height() / factor, st::black); - if (smooth && badSize) p.setRenderHint(QPainter::SmoothPixmapTransform); - QRect to((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor), request.framew / factor, request.frameh / factor); - QRect from(0, 0, original.width(), original.height()); - p.drawImage(to, original, from, Qt::ColorOnly); + if (fill) { + p.fillRect(0, 0, cache.width() / factor, cache.height() / factor, st::black); + } + QPoint position((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor)); + if (badSize) { + p.setRenderHint(QPainter::SmoothPixmapTransform); + QRect to(position, QSize(request.framew / factor, request.frameh / factor)); + QRect from(0, 0, original.width(), original.height()); + p.drawImage(to, original, from, Qt::ColorOnly); + } else { + p.drawImage(position, original); + } } if (request.rounded) { imageRound(cache); @@ -155,7 +225,9 @@ QPixmap _prepareFrame(const ClipFrameRequest &request, const QImage &original, Q return QPixmap::fromImage(original, Qt::ColorOnly); } -ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _state(ClipReading) +ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Callback *cb) +: _cb(cb) +, _state(ClipReading) , _width(0) , _height(0) , _currentDisplayed(1) @@ -182,6 +254,13 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _ _clipManagers.at(_threadIndex)->append(this, location, data); } +void ClipReader::callback(ClipReader *reader, int32 threadIndex, ClipReaderNotification notification) { + // check if reader is not deleted already + if (_clipManagers.size() > threadIndex && _clipManagers.at(threadIndex)->carries(reader)) { + if (reader->_cb) reader->_cb->call(notification); + } +} + void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded) { if (_clipManagers.size() <= _threadIndex) error(); if (_state == ClipError) return; @@ -270,6 +349,8 @@ void ClipReader::error() { ClipReader::~ClipReader() { stop(); + delete _cb; + setBadPointer(_cb); } class ClipReaderImplementation { @@ -277,7 +358,7 @@ public: ClipReaderImplementation(FileLocation *location, QByteArray *data) : _location(location), _data(data), _device(0) { } - virtual bool readNextFrame(QImage &to) = 0; + virtual bool readNextFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0; virtual int32 nextFrameDelay() = 0; virtual bool start() = 0; virtual ~ClipReaderImplementation() { @@ -312,7 +393,7 @@ public: , _frameDelay(0) { } - bool readNextFrame(QImage &to) { + bool readNextFrame(QImage &to, bool &hasAlpha, const QSize &size) { if (_reader) _frameDelay = _reader->nextImageDelay(); if (_framesLeft < 1 && !jumpToStart()) { return false; @@ -324,19 +405,24 @@ public: } --_framesLeft; - int32 w = frame.width(), h = frame.height(); - if (to.width() == w && to.height() == h && to.format() == frame.format()) { - if (to.byteCount() != frame.byteCount()) { - int bpl = qMin(to.bytesPerLine(), frame.bytesPerLine()); - for (int i = 0; i < h; ++i) { - memcpy(to.scanLine(i), frame.constScanLine(i), bpl); + if (size.isEmpty() || size == frame.size()) { + int32 w = frame.width(), h = frame.height(); + if (to.width() == w && to.height() == h && to.format() == frame.format()) { + if (to.byteCount() != frame.byteCount()) { + int bpl = qMin(to.bytesPerLine(), frame.bytesPerLine()); + for (int i = 0; i < h; ++i) { + memcpy(to.scanLine(i), frame.constScanLine(i), bpl); + } + } else { + memcpy(to.bits(), frame.constBits(), frame.byteCount()); } } else { - memcpy(to.bits(), frame.constBits(), frame.byteCount()); + to = frame.copy(); } } else { - to = frame.copy(); + to = frame.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } + hasAlpha = frame.hasAlphaChannel(); return true; } @@ -409,7 +495,7 @@ public: _avpkt.size = 0; } - bool readNextFrame(QImage &to) { + bool readNextFrame(QImage &to, bool &hasAlpha, const QSize &size) { int res; while (true) { if (_avpkt.size > 0) { // previous packet not finished @@ -472,25 +558,31 @@ public: } } - if (to.isNull() || to.width() != _width || to.height() != _height) { - to = QImage(_width, _height, QImage::Format_ARGB32); + QSize toSize(size.isEmpty() ? QSize(_width, _height) : size); + if (to.isNull() || to.size() != toSize) { + to = QImage(toSize, QImage::Format_ARGB32); } - if (_frame->width == _width && _frame->height == _height && (_frame->format == AV_PIX_FMT_BGRA || (_frame->format == -1 && _codecContext->pix_fmt == AV_PIX_FMT_BGRA))) { + if (_frame->width == toSize.width() && _frame->height == toSize.height() && (_frame->format == AV_PIX_FMT_BGRA || (_frame->format == -1 && _codecContext->pix_fmt == AV_PIX_FMT_BGRA))) { int32 sbpl = _frame->linesize[0], dbpl = to.bytesPerLine(), bpl = qMin(sbpl, dbpl); uchar *s = _frame->data[0], *d = to.bits(); for (int32 i = 0, l = _frame->height; i < l; ++i) { memcpy(d + i * dbpl, s + i * sbpl, bpl); } + + hasAlpha = true; } else { - if (_frame->width != _width || _frame->height != _height || (_frame->format != -1 && _frame->format != _codecContext->pix_fmt) || !_swsContext) { - _swsContext = sws_getCachedContext(_swsContext, _frame->width, _frame->height, AVPixelFormat(_frame->format), _width, _height, AV_PIX_FMT_BGRA, 0, 0, 0, 0); + if ((_swsSize != toSize) || (_frame->format != -1 && _frame->format != _codecContext->pix_fmt) || !_swsContext) { + _swsSize = toSize; + _swsContext = sws_getCachedContext(_swsContext, _frame->width, _frame->height, AVPixelFormat(_frame->format), toSize.width(), toSize.height(), AV_PIX_FMT_BGRA, 0, 0, 0, 0); } uint8_t * toData[1] = { to.bits() }; int toLinesize[1] = { to.bytesPerLine() }; - if ((res = sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _frame->height, toData, toLinesize)) != _height) { - LOG(("Gif Error: Unable to sws_scale to good size %1, hieght %2, should be %3").arg(logData()).arg(res).arg(_height)); + if ((res = sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _frame->height, toData, toLinesize)) != _swsSize.height()) { + LOG(("Gif Error: Unable to sws_scale to good size %1, height %2, should be %3").arg(logData()).arg(res).arg(_swsSize.height())); return false; } + + hasAlpha = false; } int64 duration = av_frame_get_pkt_duration(_frame); @@ -634,6 +726,7 @@ private: int32 _width, _height; SwsContext *_swsContext; + QSize _swsSize; int64 _frameMs; int32 _nextFrameDelay, _currentFrameDelay; @@ -665,6 +758,10 @@ public: , _location(_data.isEmpty() ? new FileLocation(location) : 0) , _accessed(false) , _implementation(0) + , _currentHasAlpha(true) + , _nextHasAlpha(true) + , _width(0) + , _height(0) , _previousMs(0) , _currentMs(0) , _nextUpdateMs(0) @@ -682,9 +779,11 @@ public: return error(); } if (_currentOriginal.isNull()) { - if (!_implementation->readNextFrame(_currentOriginal)) { + if (!_implementation->readNextFrame(_currentOriginal, _currentHasAlpha, QSize())) { return error(); } + _width = _currentOriginal.width(); + _height = _currentOriginal.height(); return ClipProcessReinit; } return ClipProcessWait; @@ -702,7 +801,7 @@ public: _previousMs = _currentMs; _currentMs = ms; - _current = _prepareFrame(_request, _currentOriginal, _currentCache, true); + _current = _prepareFrame(_request, _currentOriginal, _currentCache, _currentHasAlpha); if (!prepareNextFrame()) { return error(); @@ -710,7 +809,7 @@ public: return ClipProcessStarted; } else if (!_paused && ms >= _nextUpdateMs) { swapBuffers(); - return ClipProcessRedraw; + return ClipProcessRepaint; } return ClipProcessWait; } @@ -722,7 +821,7 @@ public: if (ms >= _nextUpdateMs) { // we are late swapBuffers(ms); // keep up - return ClipProcessRedraw; + return ClipProcessRepaint; } return ClipProcessWait; } @@ -738,16 +837,17 @@ public: qSwap(_currentOriginal, _nextOriginal); qSwap(_current, _next); qSwap(_currentCache, _nextCache); + qSwap(_currentHasAlpha, _nextHasAlpha); } bool prepareNextFrame() { - if (!_implementation->readNextFrame(_nextOriginal)) { + if (!_implementation->readNextFrame(_nextOriginal, _nextHasAlpha, QSize(_request.framew, _request.frameh))) { return false; } _nextUpdateMs = _currentMs + nextFrameDelay(); _nextOriginal.setDevicePixelRatio(_request.factor); _next = QPixmap(); - _next = _prepareFrame(_request, _nextOriginal, _nextCache, true); + _next = _prepareFrame(_request, _nextOriginal, _nextCache, _nextHasAlpha); return true; } @@ -809,6 +909,8 @@ private: ClipFrameRequest _request; QPixmap _current, _next; QImage _currentOriginal, _nextOriginal, _currentCache, _nextCache; + bool _currentHasAlpha, _nextHasAlpha; + int32 _width, _height; uint64 _previousMs, _currentMs, _nextUpdateMs; @@ -828,8 +930,8 @@ ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0), _nee _timer.moveToThread(thread); connect(&_timer, SIGNAL(timeout()), this, SLOT(process())); - connect(this, SIGNAL(reinit(ClipReader*)), _manager, SLOT(clipReinit(ClipReader*))); - connect(this, SIGNAL(redraw(ClipReader*)), _manager, SLOT(clipRedraw(ClipReader*))); + connect(this, SIGNAL(reinit(ClipReader*,qint32)), _manager, SLOT(clipReinit(ClipReader*,qint32))); + connect(this, SIGNAL(repaint(ClipReader*,qint32)), _manager, SLOT(clipRepaint(ClipReader*,qint32))); } void ClipReadManager::append(ClipReader *reader, const FileLocation &location, const QByteArray &data) { @@ -854,13 +956,18 @@ void ClipReadManager::stop(ClipReader *reader) { emit processDelayed(); } +bool ClipReadManager::carries(ClipReader *reader) const { + QMutexLocker lock(&_readerPointersMutex); + return _readerPointers.contains(reader); +} + bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { QMutexLocker lock(&_readerPointersMutex); ReaderPointers::iterator it = _readerPointers.find(reader->_interface); if (result == ClipProcessError) { if (it != _readerPointers.cend()) { it.key()->error(); - emit reinit(it.key()); + emit reinit(it.key(), it.key()->threadIndex()); _readerPointers.erase(it); it = _readerPointers.end(); @@ -871,9 +978,9 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess } if (result == ClipProcessStarted) { - _loadLevel.fetchAndAddRelease(reader->_currentOriginal.width() * reader->_currentOriginal.height() - AverageGifSize); + _loadLevel.fetchAndAddRelease(reader->_width * reader->_height - AverageGifSize); } - if (!reader->_paused && (result == ClipProcessRedraw || result == ClipProcessWait)) { + if (!reader->_paused && (result == ClipProcessRepaint || result == ClipProcessWait)) { if (it.key()->_lastDisplayMs.get() + WaitBeforeGifPause < qMax(reader->_previousMs, ms)) { reader->_paused = true; it.key()->_paused.set(true); @@ -885,14 +992,14 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess } } } - if (result == ClipProcessReinit || result == ClipProcessRedraw || result == ClipProcessStarted) { + if (result == ClipProcessReinit || result == ClipProcessRepaint || result == ClipProcessStarted) { it.key()->_current = reader->_current; it.key()->_currentOriginal = reader->_currentOriginal; it.key()->_currentDisplayed.set(false); if (result == ClipProcessReinit) { - emit reinit(it.key()); - } else if (result == ClipProcessRedraw) { - emit redraw(it.key()); + emit reinit(it.key(), it.key()->threadIndex()); + } else if (result == ClipProcessRepaint) { + emit repaint(it.key(), it.key()->threadIndex()); } } return true; @@ -900,7 +1007,7 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) { if (!handleProcessResult(reader, result, ms)) { - _loadLevel.fetchAndAddRelease(-1 * (reader->_currentOriginal.isNull() ? AverageGifSize : reader->_currentOriginal.width() * reader->_currentOriginal.height())); + _loadLevel.fetchAndAddRelease(-1 * (reader->_currentOriginal.isNull() ? AverageGifSize : reader->_width * reader->_height)); delete reader; return ResultHandleRemove; } @@ -910,7 +1017,7 @@ ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPriva return ResultHandleStop; } - if (result == ClipProcessRedraw) { + if (result == ClipProcessRepaint) { return handleResult(reader, reader->finishProcess(ms), ms); } diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index 59f7f246d..487389845 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -72,6 +72,8 @@ namespace anim { _delta = 0; } + typedef float64 Type; + private: float64 _cur, _from, _delta; @@ -110,6 +112,8 @@ namespace anim { _delta = 0; } + typedef int32 Type; + private: int32 _cur; @@ -179,6 +183,8 @@ namespace anim { _delta_r = _delta_g = _delta_b = _delta_a = 0; } + typedef QColor Type; + private: QColor _cur; @@ -338,87 +344,129 @@ AnimationCallbacks *animation(Param param, Type *obj, typename AnimationCallback return new AnimationCallbacksAbsoluteWithParam(param, obj, method); } +template +class SimpleAnimation { +public: + + typedef Function Callbacks; + + SimpleAnimation() : _data(0) { + } + + bool animating(uint64 ms) { + if (_data && _data->_a.animating()) { + _data->_a.step(ms); + return _data && _data->_a.animating(); + } + return false; + } + + bool isNull() { + return !_data; + } + + typename AnimType::Type current() { + return _data ? _data->a.current() : AnimType::Type(); + } + + typename AnimType::Type current(uint64 ms, const typename AnimType::Type &def) { + return animating(ms) ? current() : def; + } + + void setup(const typename AnimType::Type &from, Callbacks *update) { + if (!_data) { + _data = new Data(from, update, animation(this, &SimpleAnimation::step)); + } else { + delete update; + _data->a = AnimType(from, from); + } + } + + void start(const typename AnimType::Type &to, float64 duration, anim::transition transition = anim::linear) { + if (_data) { + _data->a.start(to); + _data->_a.start(); + _data->duration = duration; + _data->transition = transition; + } + } + + ~SimpleAnimation() { + delete _data; + setBadPointer(_data); + } + +private: + typedef struct _Data { + _Data(const typename AnimType::Type &from, Callbacks *update, AnimationCallbacks *acb) + : a(from, from) + , _a(acb) + , update(update) + , duration(0) + , transition(anim::linear) { + } + ~_Data() { + delete update; + setBadPointer(update); + } + AnimType a; + Animation _a; + Callbacks *update; + float64 duration; + anim::transition transition; + } Data; + Data *_data; + + void step(float64 ms, bool timer) { + float64 dt = (ms >= _data->duration) ? 1 : (ms / _data->duration); + if (dt >= 1) { + _data->a.finish(); + _data->_a.stop(); + } else { + _data->a.update(dt, _data->transition); + } + if (timer) { + _data->update->call(); + } + if (!_data->_a.animating()) { + delete _data; + _data = 0; + } + } + +}; + +typedef SimpleAnimation FloatAnimation; +typedef SimpleAnimation IntAnimation; +typedef SimpleAnimation ColorAnimation; + +#define EnsureAnimation(animation, from, callback) if ((animation).isNull()) { (animation).setup((from), (callback)); } + class ClipReader; class AnimationManager : public QObject { Q_OBJECT public: + AnimationManager(); - AnimationManager() : _timer(this), _iterating(false) { - _timer.setSingleShot(false); - connect(&_timer, SIGNAL(timeout()), this, SLOT(timeout())); - } - - void start(Animation *obj) { - if (_iterating) { - _starting.insert(obj, NullType()); - if (!_stopping.isEmpty()) { - _stopping.remove(obj); - } - } else { - if (_objects.isEmpty()) { - _timer.start(AnimationTimerDelta); - } - _objects.insert(obj, NullType()); - } - } - - void stop(Animation *obj) { - if (_iterating) { - _stopping.insert(obj, NullType()); - if (!_starting.isEmpty()) { - _starting.insert(obj, NullType()); - } - } else { - AnimatingObjects::iterator i = _objects.find(obj); - if (i != _objects.cend()) { - _objects.erase(i); - if (_objects.isEmpty()) { - _timer.stop(); - } - } - } - } + void start(Animation *obj); + void stop(Animation *obj); public slots: + void timeout(); - void timeout() { - _iterating = true; - uint64 ms = getms(); - for (AnimatingObjects::const_iterator i = _objects.begin(), e = _objects.end(); i != e; ++i) { - i.key()->step(ms, true); - } - _iterating = false; - - if (!_starting.isEmpty()) { - for (AnimatingObjects::iterator i = _starting.begin(), e = _starting.end(); i != e; ++i) { - _objects.insert(i.key(), NullType()); - } - _starting.clear(); - } - if (!_stopping.isEmpty()) { - for (AnimatingObjects::iterator i = _stopping.begin(), e = _stopping.end(); i != e; ++i) { - _objects.remove(i.key()); - } - _stopping.clear(); - } - if (!_objects.size()) { - _timer.stop(); - } - } - - void clipReinit(ClipReader *reader); - void clipRedraw(ClipReader *reader); + void clipReinit(ClipReader *reader, qint32 threadIndex); + void clipRepaint(ClipReader *reader, qint32 threadIndex); private: - typedef QMap AnimatingObjects; AnimatingObjects _objects, _starting, _stopping; QTimer _timer; bool _iterating; }; + class FileLocation; enum ClipState { @@ -460,11 +508,19 @@ private: }; +enum ClipReaderNotification { + ClipReaderReinit, + ClipReaderRepaint, +}; + class ClipReaderPrivate; class ClipReader { public: - ClipReader(const FileLocation &location, const QByteArray &data); + typedef Function1 Callback; + + ClipReader(const FileLocation &location, const QByteArray &data, Callback *cb = 0); + static void callback(ClipReader *reader, int32 threadIndex, ClipReaderNotification notification); // reader can be deleted void setAutoplay() { _autoplay = true; @@ -484,6 +540,9 @@ public: bool paused() const { return _paused.get(); } + int32 threadIndex() const { + return _threadIndex; + } int32 width() const; int32 height() const; @@ -501,6 +560,8 @@ public: private: + Callback *_cb; + ClipState _state; ClipFrameRequest _request; @@ -527,7 +588,7 @@ enum ClipProcessResult { ClipProcessError, ClipProcessStarted, ClipProcessReinit, - ClipProcessRedraw, + ClipProcessRepaint, ClipProcessWait, }; @@ -544,14 +605,15 @@ public: void start(ClipReader *reader); void update(ClipReader *reader); void stop(ClipReader *reader); + bool carries(ClipReader *reader) const; ~ClipReadManager(); signals: void processDelayed(); - void reinit(ClipReader *reader); - void redraw(ClipReader *reader); + void reinit(ClipReader *reader, qint32 threadIndex); + void repaint(ClipReader *reader, qint32 threadIndex); public slots: @@ -565,7 +627,7 @@ private: QAtomicInt _loadLevel; typedef QMap ReaderPointers; ReaderPointers _readerPointers; - QMutex _readerPointersMutex; + mutable QMutex _readerPointersMutex; bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms); diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index d4505d1ab..034aef09f 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -46,6 +46,8 @@ namespace { static const uint64 RoundedCacheSkip = 0x4000000000000000LLU; } +StorageImageLocation StorageImageLocation::Null; + bool Image::isNull() const { return (this == blank()); } @@ -688,6 +690,11 @@ void StorageImage::automaticLoad(const HistoryItem *item) { } } +void StorageImage::automaticLoadSettingsChanged() { + if (loaded() || _loader != CancelledFileLoader) return; + _loader = 0; +} + void StorageImage::load(bool loadFirst, bool prior) { if (loaded()) return; diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 4f154eb27..a8f2ed1d8 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -89,6 +89,8 @@ public: return _secret; } + static StorageImageLocation Null; + private: uint64 _widthheight; uint64 _dclocal; @@ -118,6 +120,8 @@ public: virtual void automaticLoad(const HistoryItem *item) { // auto load photo } + virtual void automaticLoadSettingsChanged() { + } virtual bool loaded() const { return true; @@ -164,6 +168,10 @@ public: virtual void loadEvenCancelled(bool loadFirst = false, bool prior = true) { } + virtual const StorageImageLocation &location() const { + return StorageImageLocation::Null; + } + bool isNull() const; void forget() const; @@ -226,6 +234,7 @@ public: int32 height() const; void automaticLoad(const HistoryItem *item); // auto load photo + void automaticLoadSettingsChanged(); bool loaded() const; bool loading() const { @@ -241,6 +250,10 @@ public: void load(bool loadFirst = false, bool prior = true); void loadEvenCancelled(bool loadFirst = false, bool prior = true); + virtual const StorageImageLocation &location() const { + return _location; + } + ~StorageImage(); protected: diff --git a/Telegram/SourceFiles/gui/twidget.h b/Telegram/SourceFiles/gui/twidget.h index 411762e1b..d01504c2b 100644 --- a/Telegram/SourceFiles/gui/twidget.h +++ b/Telegram/SourceFiles/gui/twidget.h @@ -148,6 +148,9 @@ QRect myrtlrect(const QRect &r) { \ void rtlupdate(const QRect &r) { \ update(myrtlrect(r)); \ } \ +void rtlupdate(int x, int y, int w, int h) { \ + update(myrtlrect(x, y, w, h)); \ +} \ protected: \ void enterEvent(QEvent *e) { \ TWidget *p(tparent()); \ diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 31f29d65d..c01d7f6f8 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -884,7 +884,14 @@ HistoryItem *ChannelHistory::addNewToBlocks(const MTPMessage &msg, NewMessageTyp } else { to = blocks.back(); } - return addNewItem(to, newBlock, createItem(to, msg, (type == NewMessageUnread)), (type == NewMessageUnread)); + HistoryItem *item = createItem((type == NewMessageLast) ? 0 : to, msg, (type == NewMessageUnread)); + if (type == NewMessageLast) { + if (!item->detached()) { + return item; + } + item->attach(to); + } + return addNewItem(to, newBlock, item, (type == NewMessageUnread)); } void ChannelHistory::addNewToOther(HistoryItem *item, NewMessageType type) { @@ -1331,14 +1338,17 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo } result->attach(block); } - if (result) { - if (msg.type() == mtpc_message) { - result->updateMedia(msg.c_message().has_media() ? (&msg.c_message().vmedia) : 0, (block ? false : true)); + if (msg.type() == mtpc_message) { + result->updateMedia(msg.c_message().has_media() ? (&msg.c_message().vmedia) : 0, (block ? false : true)); + if (applyServiceAction) { + App::checkSavedGif(result); } - return result; } + return result; } + bool hasNotForwardedDocument = false; + switch (msg.type()) { case mtpc_messageEmpty: result = new HistoryServiceMsg(this, block, msg.c_messageEmpty().vid.v, date(), lang(lng_message_empty)); @@ -1387,7 +1397,7 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo break; case mtpc_messageMediaDocument: switch (m.vmedia.c_messageMediaDocument().vdocument.type()) { - case mtpc_document: break; + case mtpc_document: hasNotForwardedDocument = true; break; case mtpc_documentEmpty: badMedia = 2; break; default: badMedia = 1; break; } @@ -1406,9 +1416,11 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo } if (badMedia) { result = new HistoryServiceMsg(this, block, m.vid.v, date(m.vdate), lang((badMedia == 2) ? lng_message_empty : lng_media_unsupported), m.vflags.v, 0, m.has_from_id() ? m.vfrom_id.v : 0); + hasNotForwardedDocument = false; } else { if ((m.has_fwd_date() && m.vfwd_date.v > 0) || (m.has_fwd_from_id() && peerFromMTP(m.vfwd_from_id) != 0)) { result = new HistoryForwarded(this, block, m); + hasNotForwardedDocument = false; } else if (m.has_reply_to_msg_id() && m.vreply_to_msg_id.v > 0) { result = new HistoryReply(this, block, m); } else { @@ -1543,6 +1555,10 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo } break; } + if (applyServiceAction) { + App::checkSavedGif(result); + } + return regItem(result); } @@ -3122,21 +3138,22 @@ void HistoryFileMedia::step_thumbOver(const HistoryItem *parent, float64 ms, boo _animation->a_thumbOver.finish(); _animation->_a_thumbOver.stop(); checkAnimationFinished(); - } else { + } else if (!timer) { _animation->a_thumbOver.update(dt, anim::linear); } if (timer) { - Ui::redrawHistoryItem(parent); + Ui::repaintHistoryItem(parent); } } void HistoryFileMedia::step_radial(const HistoryItem *parent, uint64 ms, bool timer) { - _animation->radial.update(dataProgress(), dataFinished(), ms); - if (!_animation->radial.animating()) { - checkAnimationFinished(); - } if (timer) { - Ui::redrawHistoryItem(parent); + Ui::repaintHistoryItem(parent); + } else { + _animation->radial.update(dataProgress(), dataFinished(), ms); + if (!_animation->radial.animating()) { + checkAnimationFinished(); + } } } @@ -3344,8 +3361,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.setPen(Qt::NoPen); if (selected) { p.setBrush(st::msgDateImgBgSelected); - } else if (_animation && _animation->_a_thumbOver.animating()) { - _animation->_a_thumbOver.step(ms); + } else if (isThumbAnimation(ms)) { float64 over = _animation->a_thumbOver.current(); p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); @@ -3636,8 +3652,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.setPen(Qt::NoPen); if (selected) { p.setBrush(st::msgDateImgBgSelected); - } else if (_animation && _animation->_a_thumbOver.animating()) { - _animation->_a_thumbOver.step(ms); + } else if (isThumbAnimation(ms)) { float64 over = _animation->a_thumbOver.current(); p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); @@ -3835,8 +3850,7 @@ void HistoryAudio::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.setPen(Qt::NoPen); if (selected) { p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); - } else if (_animation && _animation->_a_thumbOver.animating()) { - _animation->_a_thumbOver.step(ms); + } else if (isThumbAnimation(ms)) { float64 over = _animation->a_thumbOver.current(); p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); } else { @@ -4079,8 +4093,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r p.setPen(Qt::NoPen); if (selected) { p.setBrush(st::msgDateImgBgSelected); - } else if (_animation && _animation->_a_thumbOver.animating()) { - _animation->_a_thumbOver.step(ms); + } else if (isThumbAnimation(ms)) { float64 over = _animation->a_thumbOver.current(); p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); @@ -4128,7 +4141,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r p.setPen(Qt::NoPen); if (selected) { p.setBrush(outbg ? st::msgFileOutBgSelected : st::msgFileInBgSelected); - } else if (_animation && _animation->_a_thumbOver.animating()) { + } else if (isThumbAnimation(ms)) { float64 over = _animation->a_thumbOver.current(); p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); } else { @@ -4432,7 +4445,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo _data->automaticLoad(parent); bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); - if (loaded && !gif() && _gif != BadClipReader) { + if (loaded && !gif() && _gif != BadClipReader && cAutoPlayGif()) { const_cast(this)->playInline(const_cast(parent)); if (gif()) _gif->setAutoplay(); } @@ -4467,7 +4480,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo QRect rthumb(rtlrect(skipx, skipy, width, height, _width)); if (animating) { - p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, Ui::isMediaViewShown() ? 0 : ms)); + p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, (Ui::isLayerShown() || Ui::isMediaViewShown() || Ui::isGifBeingChosen()) ? 0 : ms)); } else { p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_thumbw, _thumbh, width, height)); } @@ -4475,14 +4488,13 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - if (radial || (!_gif && !loaded && !_data->loading()) || (_gif == BadClipReader)) { + if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == BadClipReader)) { 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); + } else if (isThumbAnimation(ms)) { float64 over = _animation->a_thumbOver.current(); p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); @@ -4544,7 +4556,7 @@ void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, if (x >= skipx && y >= skipy && x < skipx + width && y < skipy + height) { if (_data->uploading()) { lnk = _cancell; - } else if (!gif()) { + } else if (!gif() || !cAutoPlayGif()) { lnk = _data->loaded() ? _openl : (_data->loading() ? _cancell : _savel); } if (parent->getMedia() == this) { @@ -4611,6 +4623,9 @@ bool HistoryGif::playInline(HistoryItem *parent) { if (gif()) { stopInline(parent); } else { + if (!cAutoPlayGif()) { + App::stopGifItems(); + } _gif = new ClipReader(_data->location(), _data->data()); App::regGifItem(_gif, parent); } @@ -5135,7 +5150,8 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { _maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right(); _minh += st::msgPadding.bottom(); if (_asArticle) { - _minh += st::msgDateFont->height; + _minh = resize(_maxw, parent); // hack +// _minh += st::msgDateFont->height; } } @@ -5411,7 +5427,12 @@ const QString HistoryWebPage::inHistoryText() const { } ImagePtr HistoryWebPage::replyPreview() { - return _data->photo ? _data->photo->makeReplyPreview() : (_data->doc ? _data->doc->makeReplyPreview() : ImagePtr()); + return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr()); +} + +HistoryWebPage::~HistoryWebPage() { + delete _attach; + setBadPointer(_attach); } namespace { @@ -5999,12 +6020,6 @@ int32 HistoryMessage::plainMaxWidth() const { void HistoryMessage::initDimensions() { if (drawBubble()) { - _maxw = plainMaxWidth(); - if (_text.isEmpty()) { - _minh = 0; - } else { - _minh = st::msgPadding.top() + _text.minHeight() + st::msgPadding.bottom(); - } if (_media) { _media->initDimensions(this); if (_media->isDisplayed()) { @@ -6013,15 +6028,24 @@ void HistoryMessage::initDimensions() { _textWidth = 0; _textHeight = 0; } - int32 maxw = _media->maxWidth(); - if (maxw > _maxw) _maxw = maxw; - _minh += _media->minHeight(); } else if (!_text.hasSkipBlock()) { _text.setSkipBlock(skipBlockWidth(), skipBlockHeight()); _textWidth = 0; _textHeight = 0; } } + + _maxw = plainMaxWidth(); + if (_text.isEmpty()) { + _minh = 0; + } else { + _minh = st::msgPadding.top() + _text.minHeight() + st::msgPadding.bottom(); + } + if (_media && _media->isDisplayed()) { + int32 maxw = _media->maxWidth(); + if (maxw > _maxw) _maxw = maxw; + _minh += _media->minHeight(); + } } else { _media->initDimensions(this); _maxw = _media->maxWidth(); @@ -6211,7 +6235,7 @@ void HistoryMessage::setViewsCount(int32 count) { _viewsText = (_views >= 0) ? formatViewsCount(_views) : QString(); _viewsWidth = _viewsText.isEmpty() ? 0 : st::msgDateFont->width(_viewsText); if (was == _viewsWidth) { - Ui::redrawHistoryItem(this); + Ui::repaintHistoryItem(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); @@ -6227,7 +6251,7 @@ void HistoryMessage::setId(MsgId newId) { bool wasPositive = (id > 0), positive = (newId > 0); HistoryItem::setId(newId); if (wasPositive == positive) { - Ui::redrawHistoryItem(this); + Ui::repaintHistoryItem(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 1db540e4f..6342883df 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1235,6 +1235,12 @@ protected: _animation->radial.step(ms); return _animation && _animation->radial.animating(); } + bool isThumbAnimation(uint64 ms) const { + if (!_animation || !_animation->_a_thumbOver.animating()) return false; + + _animation->_a_thumbOver.step(ms); + return _animation && _animation->_a_thumbOver.animating(); + } virtual float64 dataProgress() const = 0; virtual bool dataFinished() const = 0; @@ -1812,6 +1818,12 @@ public: return false; } + HistoryMedia *attach() const { + return _attach; + } + + ~HistoryWebPage(); + private: WebPageData *_data; TextLinkPtr _openl; @@ -2175,6 +2187,13 @@ public: return _text.adjustSelection(from, to, type); } + void linkOver(const TextLinkPtr &lnk) { + if (_media) _media->linkOver(this, lnk); + } + void linkOut(const TextLinkPtr &lnk) { + if (_media) _media->linkOut(this, lnk); + } + void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const; QString notificationText() const; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 81e2436a8..3e0b70b5c 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -38,7 +38,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html -HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : QWidget(0) +HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : TWidget(0) , _peer(history->peer) , _migrated(history->peer->migrateFrom() ? App::history(history->peer->migrateFrom()->id) : 0) , _history(history) @@ -116,7 +116,7 @@ void HistoryInner::messagesReceivedDown(PeerData *peer, const QVectordetached() || !_history) return; int32 msgy = itemTop(item); if (msgy >= 0) { @@ -475,16 +475,16 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt if (button != Qt::LeftButton) return; if (App::pressedItem() != App::hoveredItem()) { - redrawItem(App::pressedItem()); + repaintItem(App::pressedItem()); App::pressedItem(App::hoveredItem()); - redrawItem(App::pressedItem()); + repaintItem(App::pressedItem()); } if (textlnkDown() != textlnkOver()) { - redrawItem(App::pressedLinkItem()); + repaintItem(App::pressedLinkItem()); textlnkDown(textlnkOver()); App::pressedLinkItem(App::hoveredLinkItem()); - redrawItem(App::pressedLinkItem()); - redrawItem(App::pressedItem()); + repaintItem(App::pressedLinkItem()); + repaintItem(App::pressedItem()); } _dragAction = NoDrag; @@ -512,7 +512,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt uint32 selStatus = (symbol << 16) | symbol; if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) { if (!_selected.isEmpty()) { - redrawItem(_selected.cbegin().key()); + repaintItem(_selected.cbegin().key()); _selected.clear(); } _selected.insert(_dragItem, selStatus); @@ -553,12 +553,12 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt uint32 selStatus = (_dragSymbol << 16) | _dragSymbol; if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) { if (!_selected.isEmpty()) { - redrawItem(_selected.cbegin().key()); + repaintItem(_selected.cbegin().key()); _selected.clear(); } _selected.insert(_dragItem, selStatus); _dragAction = Selecting; - redrawItem(_dragItem); + repaintItem(_dragItem); } else { _dragAction = PrepareSelect; } @@ -717,7 +717,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but } } if (textlnkDown()) { - redrawItem(App::pressedLinkItem()); + repaintItem(App::pressedLinkItem()); textlnkDown(TextLinkPtr()); App::pressedLinkItem(0); if (!textlnkOver() && _cursor != style::cur_default) { @@ -726,7 +726,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but } } if (App::pressedItem()) { - redrawItem(App::pressedItem()); + repaintItem(App::pressedItem()); App::pressedItem(0); } @@ -750,16 +750,16 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but } else { _selected.erase(i); } - redrawItem(_dragItem); + repaintItem(_dragItem); } else if (_dragAction == PrepareDrag && !_dragWasInactive && button != Qt::RightButton) { SelectedItems::iterator i = _selected.find(_dragItem); if (i != _selected.cend() && i.value() == FullSelection) { _selected.erase(i); - redrawItem(_dragItem); + repaintItem(_dragItem); } else if (i == _selected.cend() && !_dragItem->serviceMsg() && _dragItem->id > 0 && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { if (_selected.size() < MaxSelectedItems) { _selected.insert(_dragItem, FullSelection); - redrawItem(_dragItem); + repaintItem(_dragItem); } } else { _selected.clear(); @@ -804,7 +804,7 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { _dragAction = Selecting; uint32 selStatus = (symbol << 16) | symbol; if (!_selected.isEmpty()) { - redrawItem(_selected.cbegin().key()); + repaintItem(_selected.cbegin().key()); _selected.clear(); } _selected.insert(_dragItem, selStatus); @@ -928,15 +928,35 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } if (item && !isUponSelected && !_contextMenuLnk) { if (HistoryMedia *media = (msg ? msg->getMedia() : 0)) { + if (media->type() == MediaTypeWebPage && static_cast(media)->attach()) { + media = static_cast(media)->attach(); + } if (media->type() == MediaTypeSticker) { DocumentData *doc = media->getDocument(); if (doc && doc->sticker() && doc->sticker()->set.type() != mtpc_inputStickerSetEmpty) { _menu->addAction(lang(doc->sticker()->setInstalled() ? lng_context_pack_info : lng_context_pack_add), _widget, SLOT(onStickerPackInfo())); } + + _menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextFile()))->setEnabled(true); + } else if (media->type() == MediaTypeGif) { + DocumentData *doc = media->getDocument(); + if (doc) { + if (doc->loading()) { + _menu->addAction(lang(lng_context_cancel_download), this, SLOT(cancelContextDownload()))->setEnabled(true); + } else { + if (doc->mime.toLower() == qstr("video/mp4") && doc->type == AnimatedDocument) { + _menu->addAction(lang(lng_context_save_gif), this, SLOT(saveContextGif()))->setEnabled(true); + } + if (!doc->already(true).isEmpty()) { + _menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), this, SLOT(showContextInFolder()))->setEnabled(true); + } + _menu->addAction(lang(lng_context_save_file), this, SLOT(saveContextFile()))->setEnabled(true); + } + } } } QString contextMenuText = item->selectedText(FullSelection); - if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || msg->getMedia()->type() != MediaTypeSticker)) { + if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || _dragCursorState == HistoryInTextCursorState)) { _menu->addAction(lang(lng_context_copy_text), this, SLOT(copyContextText()))->setEnabled(true); } } @@ -1047,23 +1067,36 @@ void HistoryInner::copyContextImage() { } void HistoryInner::cancelContextDownload() { - VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data()); - AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data()); - DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data()); - if (lnkVideo) { + if (VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data())) { lnkVideo->video()->cancel(); - } else if (lnkAudio) { + } else if (AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data())) { lnkAudio->audio()->cancel(); - } else if (lnkDocument) { + } else if (DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data())) { lnkDocument->document()->cancel(); + } else if (HistoryItem *item = App::contextItem()) { + if (HistoryMedia *media = item->getMedia()) { + if (DocumentData *doc = media->getDocument()) { + doc->cancel(); + } + } } } void HistoryInner::showContextInFolder() { - VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data()); - AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data()); - DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data()); - QString already = lnkVideo ? lnkVideo->video()->already(true) : (lnkAudio ? lnkAudio->audio()->already(true) : (lnkDocument ? lnkDocument->document()->already(true) : QString())); + QString already; + if (VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data())) { + already = lnkVideo->video()->already(true); + } else if (AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data())) { + already = lnkAudio->audio()->already(true); + } else if (DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data())) { + already = lnkDocument->document()->already(true); + } else if (HistoryItem *item = App::contextItem()) { + if (HistoryMedia *media = item->getMedia()) { + if (DocumentData *doc = media->getDocument()) { + already = doc->already(true); + } + } + } if (!already.isEmpty()) psShowInFolder(already); } @@ -1080,12 +1113,29 @@ void HistoryInner::openContextFile() { } void HistoryInner::saveContextFile() { - VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data()); - AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data()); - DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data()); - if (lnkVideo) VideoSaveLink::doSave(lnkVideo->video(), true); - if (lnkAudio) AudioSaveLink::doSave(lnkAudio->audio(), true); - if (lnkDocument) DocumentSaveLink::doSave(lnkDocument->document(), true); + if (VideoLink *lnkVideo = dynamic_cast(_contextMenuLnk.data())) { + VideoSaveLink::doSave(lnkVideo->video(), true); + } else if (AudioLink *lnkAudio = dynamic_cast(_contextMenuLnk.data())) { + AudioSaveLink::doSave(lnkAudio->audio(), true); + } else if (DocumentLink *lnkDocument = dynamic_cast(_contextMenuLnk.data())) { + DocumentSaveLink::doSave(lnkDocument->document(), true); + } else if (HistoryItem *item = App::contextItem()) { + if (HistoryMedia *media = item->getMedia()) { + if (DocumentData *doc = media->getDocument()) { + DocumentSaveLink::doSave(doc, true); + } + } + } +} + +void HistoryInner::saveContextGif() { + if (HistoryItem *item = App::contextItem()) { + if (HistoryMedia *media = item->getMedia()) { + if (DocumentData *doc = media->getDocument()) { + _widget->saveGif(doc); + } + } + } } void HistoryInner::copyContextText() { @@ -1345,13 +1395,13 @@ void HistoryInner::enterEvent(QEvent *e) { void HistoryInner::leaveEvent(QEvent *e) { if (HistoryItem *item = App::hoveredItem()) { - redrawItem(item); + repaintItem(item); App::hoveredItem(0); } if (textlnkOver()) { if (HistoryItem *item = App::hoveredLinkItem()) { item->linkOut(textlnkOver()); - redrawItem(item); + repaintItem(item); App::hoveredLinkItem(0); } textlnkOver(TextLinkPtr()); @@ -1524,12 +1574,12 @@ void HistoryInner::onUpdateSelected() { m = mapMouseToItem(point, item); if (item->hasPoint(m.x(), m.y())) { if (App::hoveredItem() != item) { - redrawItem(App::hoveredItem()); + repaintItem(App::hoveredItem()); App::hoveredItem(item); - redrawItem(App::hoveredItem()); + repaintItem(App::hoveredItem()); } } else if (App::hoveredItem()) { - redrawItem(App::hoveredItem()); + repaintItem(App::hoveredItem()); App::hoveredItem(0); } } @@ -1557,7 +1607,7 @@ void HistoryInner::onUpdateSelected() { if (textlnkOver()) { if (HistoryItem *item = App::hoveredLinkItem()) { item->linkOut(textlnkOver()); - redrawItem(item); + repaintItem(item); } else { update(_botDescRect); } @@ -1568,7 +1618,7 @@ void HistoryInner::onUpdateSelected() { if (textlnkOver()) { if (HistoryItem *item = App::hoveredLinkItem()) { item->linkOver(textlnkOver()); - redrawItem(item); + repaintItem(item); } else { update(_botDescRect); } @@ -1610,7 +1660,7 @@ void HistoryInner::onUpdateSelected() { uint32 selState = _dragItem->adjustSelection(qMin(second, _dragSymbol), qMax(second, _dragSymbol), _dragSelType); if (_selected[_dragItem] != selState) { _selected[_dragItem] = selState; - Ui::redrawHistoryItem(_dragItem); + repaintItem(_dragItem); } if (!_wasSelectedText && (selState == FullSelection || (selState & 0xFFFF) != ((selState >> 16) & 0xFFFF))) { _wasSelectedText = true; @@ -2561,6 +2611,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _replyForwardPressed(false) , _replyReturn(0) , _stickersUpdateRequest(0) +, _savedGifsUpdateRequest(0) , _peer(0) , _clearPeer(0) , _channel(NoChannel) @@ -2744,7 +2795,11 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) void HistoryWidget::start() { connect(App::main(), SIGNAL(stickersUpdated()), &_emojiPan, SLOT(refreshStickers())); + connect(App::main(), SIGNAL(savedGifsUpdated()), &_emojiPan, SLOT(refreshSavedGifs())); + updateRecentStickers(); + if (App::main()) emit App::main()->savedGifsUpdated(); + connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*))); } @@ -2948,11 +3003,16 @@ void HistoryWidget::onRecordUpdate(qint16 level, qint32 samples) { } void HistoryWidget::updateStickers() { - if (cLastStickersUpdate() && getms(true) < cLastStickersUpdate() + StickersUpdateTimeout) return; - if (_stickersUpdateRequest) return; - - cSetStickersHash(stickersCountHash(true)); - _stickersUpdateRequest = MTP::send(MTPmessages_GetAllStickers(MTP_int(cStickersHash())), rpcDone(&HistoryWidget::stickersGot), rpcFail(&HistoryWidget::stickersFailed)); + if (!cLastStickersUpdate() || getms(true) >= cLastStickersUpdate() + StickersUpdateTimeout) { + if (!_stickersUpdateRequest) { + _stickersUpdateRequest = MTP::send(MTPmessages_GetAllStickers(MTP_int(Local::countStickersHash(true))), rpcDone(&HistoryWidget::stickersGot), rpcFail(&HistoryWidget::stickersFailed)); + } + } + if (!cLastSavedGifsUpdate() || getms(true) >= cLastSavedGifsUpdate() + StickersUpdateTimeout) { + if (!_savedGifsUpdateRequest) { + _savedGifsUpdateRequest = MTP::send(MTPmessages_GetSavedGifs(MTP_int(Local::countSavedGifsHash())), rpcDone(&HistoryWidget::savedGifsGot), rpcFail(&HistoryWidget::savedGifsFailed)); + } + } } void HistoryWidget::notify_botCommandsChanged(UserData *user) { @@ -2993,7 +3053,7 @@ void HistoryWidget::notify_migrateUpdated(PeerData *peer) { } } -void HistoryWidget::notify_mediaViewHidden() { +void HistoryWidget::notify_clipStopperHidden(ClipStopperType type) { if (_list) _list->update(); } @@ -3062,10 +3122,8 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) { } } - int32 countedHash = stickersCountHash(); - cSetStickersHash(countedHash); - if (countedHash != d.vhash.v) { - LOG(("API Error: received stickers hash %1 while counted hash is %2").arg(d.vhash.v).arg(countedHash)); + if (Local::countStickersHash() != d.vhash.v) { + LOG(("API Error: received stickers hash %1 while counted hash is %2").arg(d.vhash.v).arg(Local::countStickersHash())); } if (!setsToRequest.isEmpty() && App::api()) { @@ -3091,6 +3149,61 @@ bool HistoryWidget::stickersFailed(const RPCError &error) { return true; } +void HistoryWidget::savedGifsGot(const MTPmessages_SavedGifs &gifs) { + cSetLastSavedGifsUpdate(getms(true)); + _savedGifsUpdateRequest = 0; + + if (gifs.type() != mtpc_messages_savedGifs) return; + const MTPDmessages_savedGifs &d(gifs.c_messages_savedGifs()); + + const QVector &d_gifs(d.vgifs.c_vector().v); + + SavedGifs &saved(cRefSavedGifs()); + saved.clear(); + + saved.reserve(d_gifs.size()); + for (int32 i = 0, l = d_gifs.size(); i != l; ++i) { + DocumentData *doc = App::feedDocument(d_gifs.at(i)); + if (!doc || !doc->isAnimation()) { + LOG(("API Error: bad document returned in HistoryWidget::savedGifsGot!")); + continue; + } + + saved.push_back(doc); + } + if (Local::countSavedGifsHash() != d.vhash.v) { + LOG(("API Error: received saved gifs hash %1 while counted hash is %2").arg(d.vhash.v).arg(Local::countSavedGifsHash())); + } + + Local::writeSavedGifs(); + + if (App::main()) emit App::main()->savedGifsUpdated(); +} + +void HistoryWidget::saveGif(DocumentData *doc) { + if (doc->mime.toLower() == qstr("video/mp4") && doc->type == AnimatedDocument) { + if (cSavedGifs().indexOf(doc) != 0) { + MTP::send(MTPmessages_SaveGif(MTP_inputDocument(MTP_long(doc->id), MTP_long(doc->access)), MTP_bool(false)), rpcDone(&HistoryWidget::saveGifDone, doc)); + } + } +} + +void HistoryWidget::saveGifDone(DocumentData *doc, const MTPBool &result) { + if (mtpIsTrue(result)) { + App::addSavedGif(doc); + } +} + +bool HistoryWidget::savedGifsFailed(const RPCError &error) { + if (mtpIsFlood(error)) return false; + + LOG(("App Fail: Failed to get saved gifs!")); + + cSetLastSavedGifsUpdate(getms(true)); + _savedGifsUpdateRequest = 0; + return true; +} + void HistoryWidget::clearReplyReturns() { _replyReturns.clear(); _replyReturn = 0; @@ -3232,7 +3345,9 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re } } -// App::stopGifItems(); + if (!cAutoPlayGif()) { + App::stopGifItems(); + } clearReplyReturns(); clearAllLoadRequests(); @@ -5448,7 +5563,7 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, 0); } - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } @@ -5460,7 +5575,7 @@ void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, doc ? doc->uploadOffset : 0); } - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } @@ -5471,7 +5586,7 @@ void HistoryWidget::onAudioProgress(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, audio ? audio->uploadOffset : 0); } - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } @@ -5482,7 +5597,7 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadPhoto, -1); } -// Ui::redrawHistoryItem(item); +// Ui::repaintHistoryItem(item); } } @@ -5493,7 +5608,7 @@ void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadFile, -1); } - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } @@ -5504,7 +5619,7 @@ void HistoryWidget::onAudioFailed(const FullMsgId &newId) { if (!item->fromChannel()) { updateSendAction(item->history(), SendActionUploadAudio, -1); } - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } @@ -5602,12 +5717,24 @@ bool HistoryWidget::isItemVisible(HistoryItem *item) { return true; } -void HistoryWidget::ui_redrawHistoryItem(const HistoryItem *item) { +void HistoryWidget::ui_repaintHistoryItem(const HistoryItem *item) { if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) { - _list->redrawItem(item); + _list->repaintItem(item); } } +void HistoryWidget::ui_repaintSavedGif(const LayoutSavedGif *layout) { + _emojiPan.ui_repaintSavedGif(layout); +} + +bool HistoryWidget::ui_isSavedGifVisible(const LayoutSavedGif *layout) { + return _emojiPan.ui_isSavedGifVisible(layout) || _attachMention.ui_isSavedGifVisible(layout); +} + +bool HistoryWidget::ui_isGifBeingChosen() { + return _emojiPan.ui_isGifBeingChosen() || _attachMention.ui_isGifBeingChosen(); +} + void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { if (_peer && _list && (item == App::mousedItem() || item == App::hoveredItem() || item == App::hoveredLinkItem())) { _list->onUpdateSelected(); @@ -6527,7 +6654,7 @@ void HistoryWidget::onAnimActiveStep() { if (getms() - _animActiveStart > st::activeFadeInDuration + st::activeFadeOutDuration) { stopAnimActive(); } else { - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 3a09b2528..f8a1d472b 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -33,7 +33,7 @@ enum DragState { }; class HistoryWidget; -class HistoryInner : public QWidget { +class HistoryInner : public TWidget { Q_OBJECT public: @@ -69,7 +69,7 @@ public: int32 recountHeight(const HistoryItem *resizedItem); void updateSize(); - void redrawItem(const HistoryItem *item); + void repaintItem(const HistoryItem *item); bool canCopySelected() const; bool canDeleteSelected() const; @@ -114,6 +114,7 @@ public slots: void showContextInFolder(); void openContextFile(); void saveContextFile(); + void saveContextGif(); void copyContextText(); void copySelectedText(); @@ -541,6 +542,8 @@ public: void updateNotifySettings(); + void saveGif(DocumentData *doc); + bool contentOverlapped(const QRect &globalRect); void grabStart() { @@ -556,13 +559,16 @@ public: bool isItemVisible(HistoryItem *item); - void ui_redrawHistoryItem(const HistoryItem *item); + void ui_repaintHistoryItem(const HistoryItem *item); + void ui_repaintSavedGif(const LayoutSavedGif *gif); + bool ui_isSavedGifVisible(const LayoutSavedGif *layout); + bool ui_isGifBeingChosen(); void notify_historyItemLayoutChanged(const HistoryItem *item); void notify_botCommandsChanged(UserData *user); void notify_userIsBotChanged(UserData *user); void notify_migrateUpdated(PeerData *peer); - void notify_mediaViewHidden(); + void notify_clipStopperHidden(ClipStopperType type); void notify_historyItemResized(const HistoryItem *item, bool scrollToIt); ~HistoryWidget(); @@ -701,6 +707,8 @@ private: void addMessagesToFront(PeerData *peer, const QVector &messages, const QVector *collapsed); void addMessagesToBack(PeerData *peer, const QVector &messages, const QVector *collapsed); + void saveGifDone(DocumentData *doc, const MTPBool &result); + void reportSpamDone(PeerData *peer, const MTPBool &result, mtpRequestId request); bool reportSpamFail(const RPCError &error, mtpRequestId request); @@ -713,10 +721,13 @@ private: void countHistoryShowFrom(); + mtpRequestId _stickersUpdateRequest; void stickersGot(const MTPmessages_AllStickers &stickers); bool stickersFailed(const RPCError &error); - mtpRequestId _stickersUpdateRequest; + mtpRequestId _savedGifsUpdateRequest; + void savedGifsGot(const MTPmessages_SavedGifs &gifs); + bool savedGifsFailed(const RPCError &error); void writeDraft(MsgId *replyTo = 0, const QString *text = 0, const MessageCursor *cursor = 0, bool *previewCancelled = 0); void setFieldText(const QString &text); diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index e12b70e02..63b796942 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -195,6 +195,7 @@ StickerPreviewWidget::StickerPreviewWidget(QWidget *parent) : TWidget(parent) , a_shown(0, 0) , _a_shown(animation(this, &StickerPreviewWidget::step_shown)) , _doc(0) +, _gif(0) , _cacheStatus(CacheNotLoaded) { setAttribute(Qt::WA_TransparentForMouseEvents); } @@ -232,7 +233,7 @@ void StickerPreviewWidget::step_shown(float64 ms, bool timer) { } void StickerPreviewWidget::showPreview(DocumentData *sticker) { - if (sticker && !sticker->sticker()) sticker = 0; + if (sticker && !sticker->isAnimation() && !sticker->sticker()) sticker = 0; if (sticker) { _cache = QPixmap(); if (isHidden() || _a_shown.animating()) { @@ -245,10 +246,17 @@ void StickerPreviewWidget::showPreview(DocumentData *sticker) { } else if (isHidden()) { return; } else { + if (_gif) _cache = currentImage(); a_shown.start(0); _a_shown.start(); } _doc = sticker; + if (_gif) { + if (gif()) { + delete _gif; + } + _gif = 0; + } _cacheStatus = CacheNotLoaded; } @@ -259,7 +267,10 @@ void StickerPreviewWidget::hidePreview() { QSize StickerPreviewWidget::currentDimensions() const { if (!_doc) return QSize(_cache.width() / cIntRetinaFactor(), _cache.height() / cIntRetinaFactor()); - QSize result(qMax(_doc->dimensions.width(), 1), qMax(_doc->dimensions.height(), 1)); + QSize result(qMax(convertScale(_doc->dimensions.width()), 1), qMax(convertScale(_doc->dimensions.height()), 1)); + if (gif() && _gif->ready()) { + result = QSize(qMax(convertScale(_gif->width()), 1), qMax(convertScale(_gif->height()), 1)); + } if (result.width() > st::maxStickerSize) { result.setHeight(qMax(qRound((st::maxStickerSize * result.height()) / result.width()), 1)); result.setWidth(st::maxStickerSize); @@ -272,22 +283,64 @@ QSize StickerPreviewWidget::currentDimensions() const { } QPixmap StickerPreviewWidget::currentImage() const { - if (_doc && _cacheStatus != CacheLoaded) { - _doc->checkSticker(); - if (_doc->sticker()->img->isNull()) { - if (_cacheStatus != CacheThumbLoaded) { + if (_doc) { + if (_doc->sticker()) { + if (_cacheStatus != CacheLoaded) { + _doc->checkSticker(); + if (_doc->sticker()->img->isNull()) { + if (_cacheStatus != CacheThumbLoaded && _doc->thumb->loaded()) { + QSize s = currentDimensions(); + _cache = _doc->thumb->pixBlurred(s.width(), s.height()); + _cacheStatus = CacheThumbLoaded; + } + } else { + QSize s = currentDimensions(); + _cache = _doc->sticker()->img->pix(s.width(), s.height()); + _cacheStatus = CacheLoaded; + } + } + } else { + _doc->automaticLoad(0); + if (_doc->loaded()) { + if (!_gif && _gif != BadClipReader) { + StickerPreviewWidget *that = const_cast(this); + that->_gif = new ClipReader(_doc->location(), _doc->data(), func(that, &StickerPreviewWidget::clipCallback)); + if (gif()) _gif->setAutoplay(); + } + } + if (gif() && _gif->started()) { + QSize s = currentDimensions(); + return _gif->current(s.width(), s.height(), s.width(), s.height(), getms()); + } + if (_cacheStatus != CacheThumbLoaded && _doc->thumb->loaded()) { QSize s = currentDimensions(); _cache = _doc->thumb->pixBlurred(s.width(), s.height()); _cacheStatus = CacheThumbLoaded; } - } else { - QSize s = currentDimensions(); - _cache = _doc->sticker()->img->pix(s.width(), s.height()); - _cacheStatus = CacheLoaded; } } return _cache; } +void StickerPreviewWidget::clipCallback(ClipReaderNotification notification) { + switch (notification) { + case ClipReaderReinit: { + if (gif() && _gif->state() == ClipError) { + delete _gif; + _gif = BadClipReader; + } + + if (gif() && _gif->ready() && !_gif->started()) { + QSize s = currentDimensions(); + _gif->start(s.width(), s.height(), s.width(), s.height(), false); + } + + update(); + } break; + + case ClipReaderRepaint: update(); break; + } +} + StickerPreviewWidget::~StickerPreviewWidget() { } diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index 133a4072e..151597ede 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -131,6 +131,12 @@ private: anim::fvalue a_shown; Animation _a_shown; DocumentData *_doc; + ClipReader *_gif; + bool gif() const { + return (!_gif || _gif == BadClipReader) ? false : true; + } + + void clipCallback(ClipReaderNotification notification); enum CacheStatus { CacheNotLoaded, diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index 5e5b64173..310242993 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -227,21 +227,22 @@ void LayoutRadialProgressItem::step_iconOver(float64 ms, bool timer) { if (dt >= 1) { a_iconOver.finish(); _a_iconOver.stop(); - } else { + } else if (!timer) { a_iconOver.update(dt, anim::linear); } if (timer && iconAnimated()) { - Ui::redrawHistoryItem(_parent); + Ui::repaintHistoryItem(_parent); } } void LayoutRadialProgressItem::step_radial(uint64 ms, bool timer) { - _radial->update(dataProgress(), dataFinished(), ms); - if (!_radial->animating()) { - checkRadialFinished(); - } if (timer) { - Ui::redrawHistoryItem(_parent); + Ui::repaintHistoryItem(_parent); + } else { + _radial->update(dataProgress(), dataFinished(), ms); + if (!_radial->animating()) { + checkRadialFinished(); + } } } @@ -590,6 +591,7 @@ void LayoutOverviewAudio::paint(Painter &p, const QRect &clip, uint32 selection, if (selected) { p.setBrush(st::msgFileInBgSelected); } else if (_a_iconOver.animating()) { + _a_iconOver.step(context->ms); float64 over = a_iconOver.current(); p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); } else { @@ -787,6 +789,7 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti if (selected) { p.setBrush(st::msgFileInBgSelected); } else if (_a_iconOver.animating()) { + _a_iconOver.step(context->ms); float64 over = a_iconOver.current(); p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); } else { @@ -1280,3 +1283,230 @@ LayoutOverviewLink::Link::Link(const QString &url, const QString &text) , width(st::normalFont->width(text)) , lnk(linkFromUrl(url)) { } + +LayoutSavedGif::LayoutSavedGif(DocumentData *data) +: _data(data) +, _position(0) +, _width(st::savedGifMinWidth) +, _state(0) +, _gif(0) +, _animation(0) { +} + +void LayoutSavedGif::setPosition(int32 position, int32 width) { + _position = position; + _width = width; + if (_position < 0) { + if (gif()) delete _gif; + _gif = 0; + } +} + +void LayoutSavedGif::setWidth(int32 width) { + _width = width; +} + +int32 LayoutSavedGif::position() const { + return _position; +} + +int32 LayoutSavedGif::width() const { + return _width; +} + +void LayoutSavedGif::notify_over(bool over) { + if (!_data->loaded()) { + ensureAnimation(); + if (over == !(_state & StateOver)) { + EnsureAnimation(_animation->_a_over, (_state & StateOver) ? 1 : 0, (func(this, &LayoutSavedGif::update))); + _animation->_a_over.start(over ? 1 : 0, st::stickersRowDuration); + } + } + if (over) { + _state |= StateOver; + } else { + _state &= ~StateOver; + } +} + +void LayoutSavedGif::notify_deleteOver(bool over) { + if (over == !(_state & StateDeleteOver)) { + EnsureAnimation(_a_deleteOver, (_state & StateDeleteOver) ? 1 : 0, func(this, &LayoutSavedGif::update)); + if (over) { + _state |= StateDeleteOver; + } else { + _state &= ~StateDeleteOver; + } + _a_deleteOver.start((_state & StateDeleteOver) ? 1 : 0, st::stickersRowDuration); + } +} + +void LayoutSavedGif::paint(Painter &p, bool paused, uint64 ms) const { + _data->automaticLoad(0); + + bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); + if (loaded && !gif() && _gif != BadClipReader) { + LayoutSavedGif *that = const_cast(this); + that->_gif = new ClipReader(_data->location(), _data->data(), func(that, &LayoutSavedGif::clipCallback)); + if (gif()) _gif->setAutoplay(); + } + + bool animating = (gif() && _gif->started()); + if (displayLoading) { + ensureAnimation(); + if (!_animation->radial.animating()) { + _animation->radial.start(_data->progress()); + } + } + bool radial = isRadialAnimation(ms); + + int32 height = st::savedGifHeight; + QSize frame = countFrameSize(); + + QRect r(0, 0, _width, height); + if (animating) { + if (!_thumb.isNull()) const_cast(this)->_thumb = QPixmap(); + p.drawPixmap(r.topLeft(), _gif->current(frame.width(), frame.height(), _width, height, paused ? 0 : ms)); + } else { + if (!_data->thumb->isNull()) { + if (_data->thumb->loaded()) { + if (_thumb.width() != _width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { + const_cast(this)->_thumb = _data->thumb->pixNoCache(frame.width(), frame.height(), true, false, false, _width, height); + } + } else { + _data->thumb->load(); + } + } + p.drawPixmap(r.topLeft(), _thumb); + } + + if (radial || (!_gif && !loaded && !_data->loading()) || (_gif == BadClipReader)) { + float64 radialOpacity = (radial && loaded && !_data->uploading()) ? _animation->radial.opacity() : 1; + if (_animation && _animation->_a_over.animating(ms)) { + float64 over = _animation->_a_over.current(); + p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); + p.fillRect(r, st::black); + } else { + p.fillRect(r, (_state & StateOver) ? st::msgDateImgBgOver : st::msgDateImgBg); + } + p.setOpacity(radialOpacity * p.opacity()); + + p.setOpacity(radialOpacity); + style::sprite icon; + if (_data->loaded() && !radial) { + icon = st::msgFileInPlay; + } else if (radial || _data->loading()) { + icon = st::msgFileInCancel; + } else { + icon = st::msgFileInDownload; + } + QRect inner((_width - st::msgFileSize) / 2, (height - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + 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, st::msgInBg); + } + } + + if (_state & StateOver) { + float64 deleteOver = _a_deleteOver.current(ms, (_state & StateDeleteOver) ? 1 : 0); + QPoint deletePos = QPoint(_width - st::stickerPanDelete.pxWidth(), 0); + p.setOpacity(deleteOver + (1 - deleteOver) * st::stickerPanDeleteOpacity); + p.drawSpriteLeft(deletePos, _width, st::stickerPanDelete); + p.setOpacity(1); + } +} + +QSize LayoutSavedGif::countFrameSize() const { + bool animating = (gif() && _gif->ready()); + int32 framew = animating ? _gif->width() : _data->thumb->width(), frameh = animating ? _gif->height() : _data->thumb->height(), height = st::savedGifHeight; + if (framew * height > frameh * _width) { + if (framew < st::maxStickerSize || frameh > height) { + if (frameh > height || (framew * height / frameh) <= st::maxStickerSize) { + framew = framew * height / frameh; + frameh = height; + } else { + frameh = int32(frameh * st::maxStickerSize) / framew; + framew = st::maxStickerSize; + } + } + } else { + if (frameh < st::maxStickerSize || framew > _width) { + if (framew > _width || (frameh * _width / framew) <= st::maxStickerSize) { + frameh = frameh * _width / framew; + framew = _width; + } else { + framew = int32(framew * st::maxStickerSize) / frameh; + frameh = st::maxStickerSize; + } + } + } + return QSize(framew, frameh); +} + +void LayoutSavedGif::preload() { + _data->thumb->load(); +} + +LayoutSavedGif::~LayoutSavedGif() { + delete _animation; + setBadPointer(_animation); +} + +void LayoutSavedGif::ensureAnimation() const { + if (!_animation) { + _animation = new AnimationData(animation(const_cast(this), &LayoutSavedGif::step_radial)); + } +} + +bool LayoutSavedGif::isRadialAnimation(uint64 ms) const { + if (!_animation || !_animation->radial.animating()) return false; + + _animation->radial.step(ms); + return _animation && _animation->radial.animating(); +} + +void LayoutSavedGif::step_radial(uint64 ms, bool timer) { + if (timer) { + update(); + } else { + _animation->radial.update(_data->progress(), !_data->loading() || _data->loaded(), ms); + if (!_animation->radial.animating() && _data->loaded()) { + delete _animation; + _animation = 0; + } + } +} + +void LayoutSavedGif::clipCallback(ClipReaderNotification notification) { + switch (notification) { + case ClipReaderReinit: { + if (gif()) { + if (_gif->state() == ClipError) { + delete _gif; + _gif = BadClipReader; + _data->forget(); + } else if (_gif->ready() && !_gif->started()) { + int32 height = st::savedGifHeight; + QSize frame = countFrameSize(); + _gif->start(frame.width(), frame.height(), _width, height, false); + } else if (_gif->paused() && !Ui::isSavedGifVisible(this)) { + delete _gif; + _gif = 0; + _data->forget(); + } + } + + update(); + } break; + + case ClipReaderRepaint: update(); break; + } +} + +void LayoutSavedGif::update() { + if (_position >= 0) { + Ui::repaintSavedGif(this); + } +} diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index f606c6cea..ab0c37536 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -470,3 +470,62 @@ private: QVector _links; }; + +class LayoutSavedGif { +public: + LayoutSavedGif(DocumentData *data); + + void paint(Painter &p, bool paused, uint64 ms) const; + void preload(); + DocumentData *document() const { + return _data; + } + + void setPosition(int32 position, int32 width); + void setWidth(int32 width); + int32 position() const; + int32 width() const; + + void notify_over(bool over); + void notify_deleteOver(bool over); + + ~LayoutSavedGif(); + +private: + DocumentData *_data; + int32 _position; // < 0 means removed from layout + int32 _width; + QSize countFrameSize() const; + + enum StateFlags { + StateOver = 0x01, + StateDeleteOver = 0x02, + }; + int32 _state; + + ClipReader *_gif; + bool gif() const { + return (!_gif || _gif == BadClipReader) ? false : true; + } + QPixmap _thumb; + + void ensureAnimation() const; + bool isRadialAnimation(uint64 ms) const; + void step_radial(uint64 ms, bool timer); + + void clipCallback(ClipReaderNotification notification); + void update(); + + struct AnimationData { + AnimationData(AnimationCallbacks *radialCallbacks) + : over(false) + , radial(radialCallbacks) { + } + bool over; + FloatAnimation _a_over; + RadialAnimation radial; + }; + mutable AnimationData *_animation; + mutable FloatAnimation _a_deleteOver; + +}; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 589d24893..8d2d80968 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -552,6 +552,7 @@ namespace { lskStickers = 0x0b, // no data lskSavedPeers = 0x0c, // no data lskReportSpamStatuses = 0x0d, // no data + lskSavedGifs = 0x0e, }; typedef QMap DraftsMap; @@ -568,7 +569,7 @@ namespace { FileLocationAliases _fileLocationAliases; FileKey _locationsKey = 0, _reportSpamStatusesKey = 0; - FileKey _recentStickersKeyOld = 0, _stickersKey = 0; + FileKey _recentStickersKeyOld = 0, _stickersKey = 0, _savedGifsKey; FileKey _backgroundKey = 0; bool _backgroundWasRead = false; @@ -797,6 +798,14 @@ namespace { cSetMaxGroupCount(maxSize); } break; + case dbiSavedGifsLimit: { + qint32 limit; + stream >> limit; + if (!_checkStreamStatus(stream)) return false; + + cSetSavedGifsLimit(limit); + } break; + case dbiMaxMegaGroupCount: { qint32 maxSize; stream >> maxSize; @@ -873,6 +882,14 @@ namespace { cSetAutoDownloadGif(gif); } break; + case dbiAutoPlay: { + qint32 gif; + stream >> gif; + if (!_checkStreamStatus(stream)) return false; + + cSetAutoPlayGif(gif == 1); + } break; + case dbiIncludeMuted: { qint32 v; stream >> v; @@ -881,6 +898,14 @@ namespace { cSetIncludeMuted(v == 1); } break; + case dbiShowingSavedGifs: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetShowingSavedGifs(v == 1); + } break; + case dbiDesktopNotify: { qint32 v; stream >> v; @@ -1438,7 +1463,7 @@ namespace { _writeMap(WriteMapFast); } - uint32 size = 14 * (sizeof(quint32) + sizeof(qint32)); + uint32 size = 16 * (sizeof(quint32) + sizeof(qint32)); size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath()) + _bytearraySize(cAskDownloadPath() ? QByteArray() : cDownloadPathBookmark()); size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort)); size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64)); @@ -1454,6 +1479,7 @@ namespace { data.stream << quint32(dbiDefaultAttach) << qint32(cDefaultAttach()); data.stream << quint32(dbiSoundNotify) << qint32(cSoundNotify()); data.stream << quint32(dbiIncludeMuted) << qint32(cIncludeMuted()); + data.stream << quint32(dbiShowingSavedGifs) << qint32(cShowingSavedGifs()); data.stream << quint32(dbiDesktopNotify) << qint32(cDesktopNotify()); data.stream << quint32(dbiNotifyView) << qint32(cNotifyView()); data.stream << quint32(dbiWindowsNotifications) << qint32(cWindowsNotifications()); @@ -1463,6 +1489,7 @@ namespace { data.stream << quint32(dbiDialogLastPath) << cDialogLastPath(); data.stream << quint32(dbiSongVolume) << qint32(qRound(cSongVolume() * 1e6)); data.stream << quint32(dbiAutoDownload) << qint32(cAutoDownloadPhoto()) << qint32(cAutoDownloadAudio()) << qint32(cAutoDownloadGif()); + data.stream << quint32(dbiAutoPlay) << qint32(cAutoPlayGif() ? 1 : 0); { RecentEmojisPreload v(cRecentEmojisPreload()); @@ -1608,7 +1635,9 @@ namespace { DraftsNotReadMap draftsNotReadMap; StorageMap imagesMap, stickerImagesMap, audiosMap; qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0; - quint64 locationsKey = 0, reportSpamStatusesKey = 0, recentStickersKeyOld = 0, stickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0, savedPeersKey = 0; + quint64 locationsKey = 0, reportSpamStatusesKey = 0; + quint64 recentStickersKeyOld = 0, stickersKey = 0, savedGifsKey = 0; + quint64 backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0, savedPeersKey = 0; while (!map.stream.atEnd()) { quint32 keyType; map.stream >> keyType; @@ -1691,6 +1720,9 @@ namespace { case lskStickers: { map.stream >> stickersKey; } break; + case lskSavedGifs: { + map.stream >> savedGifsKey; + } break; case lskSavedPeers: { map.stream >> savedPeersKey; } break; @@ -1718,6 +1750,7 @@ namespace { _reportSpamStatusesKey = reportSpamStatusesKey; _recentStickersKeyOld = recentStickersKeyOld; _stickersKey = stickersKey; + _savedGifsKey = savedGifsKey; _savedPeersKey = savedPeersKey; _backgroundKey = backgroundKey; _userSettingsKey = userSettingsKey; @@ -1790,6 +1823,7 @@ namespace { if (_reportSpamStatusesKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64); if (_stickersKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_savedGifsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_savedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); @@ -1837,6 +1871,9 @@ namespace { if (_stickersKey) { mapData.stream << quint32(lskStickers) << quint64(_stickersKey); } + if (_savedGifsKey) { + mapData.stream << quint32(lskSavedGifs) << quint64(_savedGifsKey); + } if (_savedPeersKey) { mapData.stream << quint32(lskSavedPeers) << quint64(_savedPeersKey); } @@ -2040,7 +2077,7 @@ namespace Local { cSetDcOptions(dcOpts); } - quint32 size = 11 * (sizeof(quint32) + sizeof(qint32)); + quint32 size = 12 * (sizeof(quint32) + sizeof(qint32)); for (mtpDcOptions::const_iterator i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { size += sizeof(quint32) + sizeof(quint32) + sizeof(quint32); size += sizeof(quint32) + _stringSize(QString::fromUtf8(i->ip.data(), i->ip.size())); @@ -2058,6 +2095,7 @@ namespace Local { EncryptedDescriptor data(size); data.stream << quint32(dbiMaxGroupCount) << qint32(cMaxGroupCount()); data.stream << quint32(dbiMaxMegaGroupCount) << qint32(cMaxMegaGroupCount()); + data.stream << quint32(dbiSavedGifsLimit) << qint32(cSavedGifsLimit()); data.stream << quint32(dbiAutoStart) << qint32(cAutoStart()); data.stream << quint32(dbiStartMinimized) << qint32(cStartMinimized()); data.stream << quint32(dbiSendToMenu) << qint32(cSendToMenu()); @@ -2111,7 +2149,9 @@ namespace Local { _stickerImagesMap.clear(); _audiosMap.clear(); _storageImagesSize = _storageStickersSize = _storageAudiosSize = 0; - _locationsKey = _reportSpamStatusesKey = _recentStickersKeyOld = _stickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0; + _locationsKey = _reportSpamStatusesKey = 0; + _recentStickersKeyOld = _stickersKey = _savedGifsKey = 0; + _backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0; _oldMapVersion = _oldSettingsVersion = 0; _mapChanged = true; _writeMap(WriteMapNow); @@ -2667,7 +2707,7 @@ namespace Local { _writeMap(); } else { int32 setsCount = 0; - QByteArray hashToWrite = (qsl("%d:") + QString::number(cStickersHash())).toUtf8(); + QByteArray hashToWrite; quint32 size = sizeof(quint32) + _bytearraySize(hashToWrite); for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) { bool notLoaded = (i->flags & MTPDstickerSet_flag_NOT_LOADED); @@ -2729,8 +2769,6 @@ namespace Local { RecentStickerPack &recent(cRefRecentStickers()); recent.clear(); - cSetStickersHash(0); - StickerSet &def(sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, lang(lng_stickers_default_set), QString(), 0, 0, MTPDstickerSet::flag_official)).value()); StickerSet &custom(sets.insert(CustomStickerSetId, StickerSet(CustomStickerSetId, 0, lang(lng_custom_stickers), QString(), 0, 0, 0)).value()); @@ -2806,9 +2844,8 @@ namespace Local { quint32 cnt; QByteArray hash; - stickers.stream >> cnt >> hash; - if (stickers.version < 8019) { - hash.clear(); // bad data in old caches + stickers.stream >> cnt >> hash; // ignore hash, it is counted + if (stickers.version < 8019) { // bad data in old caches cnt += 2; // try to read at least something } for (uint32 i = 0; i < cnt; ++i) { @@ -2886,11 +2923,124 @@ namespace Local { ++set.count; } } + } - if (hash.startsWith(qsl("%d:").toUtf8())) { - cSetStickersHash(QString::fromUtf8(hash.mid(3)).toInt()); + int32 countStickersHash(bool checkOfficial) { + uint32 acc = 0; + bool foundOfficial = false, foundBad = false;; + const StickerSets &sets(cStickerSets()); + const StickerSetsOrder &order(cStickerSetsOrder()); + for (StickerSetsOrder::const_iterator i = order.cbegin(), e = order.cend(); i != e; ++i) { + StickerSets::const_iterator j = sets.constFind(*i); + if (j != sets.cend()) { + if (j->id == 0) { + foundBad = true; + } else if (j->flags & MTPDstickerSet::flag_official) { + foundOfficial = true; + } + if (!(j->flags & MTPDstickerSet::flag_disabled)) { + acc = (acc * 20261) + j->hash; + } + } + } + return (!checkOfficial || (!foundBad && foundOfficial)) ? int32(acc & 0x7FFFFFFF) : 0; + } + + int32 countSavedGifsHash() { + uint32 acc = 0; + const SavedGifs &saved(cSavedGifs()); + for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { + uint64 docId = (*i)->id; + + acc = (acc * 20261) + uint32(docId >> 32); + acc = (acc * 20261) + uint32(docId & 0xFFFFFFFF); + } + return int32(acc & 0x7FFFFFFF); + } + + void writeSavedGifs() { + if (!_working()) return; + + const SavedGifs &saved(cSavedGifs()); + if (saved.isEmpty()) { + if (_savedGifsKey) { + clearKey(_savedGifsKey); + _savedGifsKey = 0; + _mapChanged = true; + } + _writeMap(); } else { - cSetStickersHash(0); + quint32 size = sizeof(quint32); // count + for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { + DocumentData *doc = *i; + + // id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32); + + // thumb + size += _storageImageLocationSize(); + } + + if (!_savedGifsKey) { + _savedGifsKey = genKey(); + _mapChanged = true; + _writeMap(WriteMapFast); + } + EncryptedDescriptor data(size); + data.stream << quint32(saved.size()); + for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { + DocumentData *doc = *i; + + data.stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type); + _writeStorageImageLocation(data.stream, doc->thumb->location()); + } + FileWriteDescriptor file(_savedGifsKey); + file.writeEncrypted(data); + } + } + + void readSavedGifs() { + if (!_savedGifsKey) return; + + FileReadDescriptor gifs; + if (!readEncryptedFile(gifs, _savedGifsKey)) { + clearKey(_savedGifsKey); + _savedGifsKey = 0; + _writeMap(); + return; + } + + SavedGifs &saved(cRefSavedGifs()); + saved.clear(); + + quint32 cnt; + gifs.stream >> cnt; + saved.reserve(cnt); + QMap read; + for (uint32 i = 0; i < cnt; ++i) { + quint64 id, access; + QString name, mime; + qint32 date, dc, size, width, height, type; + gifs.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type; + + StorageImageLocation thumb(_readStorageImageLocation(gifs)); + + if (read.contains(id)) continue; + read.insert(id, NullType()); + + QVector attributes; + if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name))); + if (type == AnimatedDocument) { + attributes.push_back(MTP_documentAttributeAnimated()); + } + if (width > 0 && height > 0) { + attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); + } + + DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); + if (!doc->isAnimation()) continue; + + saved.push_back(doc); } } diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index eafc6066d..76c193ebe 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -142,6 +142,11 @@ namespace Local { void writeStickers(); void readStickers(); + int32 countStickersHash(bool checkOfficial = false); + + void writeSavedGifs(); + void readSavedGifs(); + int32 countSavedGifsHash(); void writeBackground(int32 id, const QImage &img); bool readBackground(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 418e39498..10998c2ab 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -742,7 +742,7 @@ QPixmap MainWidget::grabTopBar() { } void MainWidget::ui_showStickerPreview(DocumentData *sticker) { - if (!sticker || !sticker->sticker()) return; + if (!sticker || ((!sticker->isAnimation() || !sticker->loaded()) && !sticker->sticker())) return; if (!_stickerPreview) { _stickerPreview = new StickerPreviewWidget(this); resizeEvent(0); @@ -773,7 +773,7 @@ void MainWidget::notify_userIsContactChanged(UserData *user, bool fromThisApp) { if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { j.key()->initDimensions(); - Ui::redrawHistoryItem(j.key()); + Ui::repaintHistoryItem(j.key()); } } @@ -786,18 +786,28 @@ void MainWidget::notify_migrateUpdated(PeerData *peer) { history.notify_migrateUpdated(peer); } -void MainWidget::notify_mediaViewHidden() { - history.notify_mediaViewHidden(); +void MainWidget::notify_clipStopperHidden(ClipStopperType type) { + history.notify_clipStopperHidden(type); } -void MainWidget::ui_redrawHistoryItem(const HistoryItem *item) { - if (!item) return; - - history.ui_redrawHistoryItem(item); +void MainWidget::ui_repaintHistoryItem(const HistoryItem *item) { + history.ui_repaintHistoryItem(item); if (!item->history()->dialogs.isEmpty() && item->history()->lastMsg == item) { dialogs.dlgUpdated(item->history()->dialogs[0]); } - if (overview) overview->ui_redrawHistoryItem(item); + if (overview) overview->ui_repaintHistoryItem(item); +} + +void MainWidget::ui_repaintSavedGif(const LayoutSavedGif *layout) { + history.ui_repaintSavedGif(layout); +} + +bool MainWidget::ui_isSavedGifVisible(const LayoutSavedGif *layout) { + return history.ui_isSavedGifVisible(layout); +} + +bool MainWidget::ui_isGifBeingChosen() { + return history.ui_isGifBeingChosen(); } void MainWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { @@ -814,7 +824,7 @@ void MainWidget::notify_historyItemResized(const HistoryItem *item, bool scrollT history.resizeEvent(0); } } - if (item) Ui::redrawHistoryItem(item); + if (item) Ui::repaintHistoryItem(item); } void MainWidget::noHider(HistoryHider *destroyed) { @@ -1638,7 +1648,7 @@ void MainWidget::videoLoadProgress(mtpFileLoader *loader) { VideoItems::const_iterator i = items.constFind(video); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - Ui::redrawHistoryItem(j.key()); + Ui::repaintHistoryItem(j.key()); } } } @@ -1694,7 +1704,7 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) { AudioItems::const_iterator i = items.constFind(audio); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - Ui::redrawHistoryItem(j.key()); + Ui::repaintHistoryItem(j.key()); } } } @@ -1729,7 +1739,7 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) { } if (HistoryItem *item = App::histItemById(audioId.msgId)) { - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } @@ -1790,7 +1800,7 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { } if (HistoryItem *item = App::histItemById(songId.msgId)) { - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); } } @@ -1828,7 +1838,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { DocumentItems::const_iterator i = items.constFind(document); if (i != items.cend()) { for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - Ui::redrawHistoryItem(j.key()); + Ui::repaintHistoryItem(j.key()); } } App::wnd()->documentUpdated(document); @@ -3415,6 +3425,7 @@ void MainWidget::start(const MTPUser &user) { _started = true; App::wnd()->sendServiceHistoryRequest(); Local::readStickers(); + Local::readSavedGifs(); history.start(); } @@ -4155,7 +4166,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { msgRow->history()->unregTyping(App::self()); } App::historyRegItem(msgRow); - Ui::redrawHistoryItem(msgRow); + Ui::repaintHistoryItem(msgRow); } } App::historyUnregRandom(d.vrandom_id.v); @@ -4176,7 +4187,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (HistoryItem *item = App::histItemById(NoChannel, v.at(i).v)) { if (item->isMediaUnread()) { item->markMediaRead(); - Ui::redrawHistoryItem(item); + Ui::repaintHistoryItem(item); if (item->out() && item->history()->peer->isUser()) { item->history()->peer->asUser()->madeAction(); } @@ -4580,7 +4591,6 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { sets.erase(custom); } } - cSetStickersHash(stickersCountHash()); Local::writeStickers(); emit stickersUpdated(); } @@ -4600,11 +4610,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } if (result.size() != cStickerSetsOrder().size() || result.size() != order.size()) { cSetLastStickersUpdate(0); - cSetStickersHash(0); App::main()->updateStickers(); } else { cSetStickerSetsOrder(result); - cSetStickersHash(stickersCountHash()); Local::writeStickers(); emit stickersUpdated(); } @@ -4612,7 +4620,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updateStickerSets: { cSetLastStickersUpdate(0); - cSetStickersHash(0); + App::main()->updateStickers(); + } break; + + case mtpc_updateSavedGifs: { + cSetLastSavedGifsUpdate(0); App::main()->updateStickers(); } break; diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index a4ef15298..d72e81078 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -409,14 +409,17 @@ public: void ui_showStickerPreview(DocumentData *sticker); void ui_hideStickerPreview(); - void ui_redrawHistoryItem(const HistoryItem *item); + void ui_repaintHistoryItem(const HistoryItem *item); + void ui_repaintSavedGif(const LayoutSavedGif *layout); + bool ui_isSavedGifVisible(const LayoutSavedGif *layout); + bool ui_isGifBeingChosen(); void ui_showPeerHistory(quint64 peer, qint32 msgId, bool back); void notify_botCommandsChanged(UserData *bot); void notify_userIsBotChanged(UserData *bot); void notify_userIsContactChanged(UserData *user, bool fromThisApp); void notify_migrateUpdated(PeerData *peer); - void notify_mediaViewHidden(); + void notify_clipStopperHidden(ClipStopperType type); void notify_historyItemResized(const HistoryItem *row, bool scrollToIt); void notify_historyItemLayoutChanged(const HistoryItem *item); @@ -430,6 +433,7 @@ signals: void dialogRowReplaced(DialogRow *oldRow, DialogRow *newRow); void dialogsUpdated(); void stickersUpdated(); + void savedGifsUpdated(); public slots: diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index d1b31bede..8522d3a7c 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -603,7 +603,7 @@ void MediaView::onDocClick() { } } -void MediaView::ui_clipRedraw(ClipReader *reader) { +void MediaView::ui_clipRepaint(ClipReader *reader) { if (reader == _gif) { update(_x, _y, _w, _h); } @@ -1960,7 +1960,7 @@ void MediaView::hide() { QWidget::hide(); stopGif(); - Notify::mediaViewHidden(); + Notify::clipStopperHidden(ClipStopperMediaview); } void MediaView::onMenuDestroy(QObject *obj) { diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 498fd5427..d949c80a3 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -71,7 +71,7 @@ public: void activateControls(); void onDocClick(); - void ui_clipRedraw(ClipReader *reader); + void ui_clipRepaint(ClipReader *reader); void notify_clipReinit(ClipReader *reader); diff --git a/Telegram/SourceFiles/mtproto/mtp.cpp b/Telegram/SourceFiles/mtproto/mtp.cpp index ebc6b3853..6b9b26cb0 100644 --- a/Telegram/SourceFiles/mtproto/mtp.cpp +++ b/Telegram/SourceFiles/mtproto/mtp.cpp @@ -154,6 +154,9 @@ namespace { bool onErrorDefault(mtpRequestId requestId, const RPCError &error) { const QString &err(error.type()); int32 code = error.code(); + if (!mtpIsFlood(error)) { + int breakpoint = 0; + } bool badGuestDC = (code == 400) && (err == qsl("FILE_ID_INVALID")); QRegularExpressionMatch m; if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) { diff --git a/Telegram/SourceFiles/mtproto/mtpDC.cpp b/Telegram/SourceFiles/mtproto/mtpDC.cpp index 700bd3caa..ee5a079ad 100644 --- a/Telegram/SourceFiles/mtproto/mtpDC.cpp +++ b/Telegram/SourceFiles/mtproto/mtpDC.cpp @@ -154,6 +154,7 @@ namespace { mtpUpdateDcOptions(data.vdc_options.c_vector().v); cSetMaxGroupCount(data.vchat_size_max.v); cSetMaxMegaGroupCount(data.vmegagroup_size_max.v); + cSetSavedGifsLimit(data.vsaved_gifs_limit.v); configLoadedOnce = true; Local::writeSettings(); diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp index 0bdddfbd6..4fa0d5eb3 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp +++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp @@ -2466,6 +2466,10 @@ void _serialize_inputMessagesFilterUrl(MTPStringLogger &to, int32 stage, int32 l to.add("{ inputMessagesFilterUrl }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); } +void _serialize_inputMessagesFilterGif(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + to.add("{ inputMessagesFilterGif }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); +} + void _serialize_updateNewMessage(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -3039,6 +3043,10 @@ void _serialize_updateStickerSets(MTPStringLogger &to, int32 stage, int32 lev, T to.add("{ updateStickerSets }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); } +void _serialize_updateSavedGifs(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + to.add("{ updateSavedGifs }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); +} + void _serialize_updates_state(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -3336,7 +3344,8 @@ void _serialize_config(MTPStringLogger &to, int32 stage, int32 lev, Types &types case 14: to.add(" chat_big_size: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 15: to.add(" push_chat_period_ms: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; case 16: to.add(" push_chat_limit: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; - case 17: to.add(" disabled_features: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 17: to.add(" saved_gifs_limit: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 18: to.add(" disabled_features: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; } } @@ -5107,6 +5116,24 @@ void _serialize_messages_foundGifs(MTPStringLogger &to, int32 stage, int32 lev, } } +void _serialize_messages_savedGifsNotModified(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + to.add("{ messages_savedGifsNotModified }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); +} + +void _serialize_messages_savedGifs(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_savedGifs"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" gifs: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_req_pq(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -5621,6 +5648,20 @@ void _serialize_messages_reorderStickerSets(MTPStringLogger &to, int32 stage, in } } +void _serialize_messages_saveGif(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_saveGif"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + case 1: to.add(" unsave: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_upload_saveFilePart(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { if (stage) { to.add(",\n").addSpaces(lev); @@ -7098,6 +7139,19 @@ void _serialize_messages_searchGifs(MTPStringLogger &to, int32 stage, int32 lev, } } +void _serialize_messages_getSavedGifs(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_getSavedGifs"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break; + default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break; + } +} + void _serialize_updates_getState(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 flag) { to.add("{ updates_getState }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); } @@ -7546,6 +7600,7 @@ namespace { _serializers.insert(mtpc_inputMessagesFilterAudio, _serialize_inputMessagesFilterAudio); _serializers.insert(mtpc_inputMessagesFilterAudioDocuments, _serialize_inputMessagesFilterAudioDocuments); _serializers.insert(mtpc_inputMessagesFilterUrl, _serialize_inputMessagesFilterUrl); + _serializers.insert(mtpc_inputMessagesFilterGif, _serialize_inputMessagesFilterGif); _serializers.insert(mtpc_updateNewMessage, _serialize_updateNewMessage); _serializers.insert(mtpc_updateMessageID, _serialize_updateMessageID); _serializers.insert(mtpc_updateDeleteMessages, _serialize_updateDeleteMessages); @@ -7586,6 +7641,7 @@ namespace { _serializers.insert(mtpc_updateNewStickerSet, _serialize_updateNewStickerSet); _serializers.insert(mtpc_updateStickerSetsOrder, _serialize_updateStickerSetsOrder); _serializers.insert(mtpc_updateStickerSets, _serialize_updateStickerSets); + _serializers.insert(mtpc_updateSavedGifs, _serialize_updateSavedGifs); _serializers.insert(mtpc_updates_state, _serialize_updates_state); _serializers.insert(mtpc_updates_differenceEmpty, _serialize_updates_differenceEmpty); _serializers.insert(mtpc_updates_difference, _serialize_updates_difference); @@ -7754,6 +7810,8 @@ namespace { _serializers.insert(mtpc_help_termsOfService, _serialize_help_termsOfService); _serializers.insert(mtpc_foundGif, _serialize_foundGif); _serializers.insert(mtpc_messages_foundGifs, _serialize_messages_foundGifs); + _serializers.insert(mtpc_messages_savedGifsNotModified, _serialize_messages_savedGifsNotModified); + _serializers.insert(mtpc_messages_savedGifs, _serialize_messages_savedGifs); _serializers.insert(mtpc_req_pq, _serialize_req_pq); _serializers.insert(mtpc_req_DH_params, _serialize_req_DH_params); @@ -7794,6 +7852,7 @@ namespace { _serializers.insert(mtpc_messages_uninstallStickerSet, _serialize_messages_uninstallStickerSet); _serializers.insert(mtpc_messages_editChatAdmin, _serialize_messages_editChatAdmin); _serializers.insert(mtpc_messages_reorderStickerSets, _serialize_messages_reorderStickerSets); + _serializers.insert(mtpc_messages_saveGif, _serialize_messages_saveGif); _serializers.insert(mtpc_upload_saveFilePart, _serialize_upload_saveFilePart); _serializers.insert(mtpc_upload_saveBigFilePart, _serialize_upload_saveBigFilePart); _serializers.insert(mtpc_help_saveAppLog, _serialize_help_saveAppLog); @@ -7902,6 +7961,7 @@ namespace { _serializers.insert(mtpc_messages_getStickerSet, _serialize_messages_getStickerSet); _serializers.insert(mtpc_messages_getDocumentByHash, _serialize_messages_getDocumentByHash); _serializers.insert(mtpc_messages_searchGifs, _serialize_messages_searchGifs); + _serializers.insert(mtpc_messages_getSavedGifs, _serialize_messages_getSavedGifs); _serializers.insert(mtpc_updates_getState, _serialize_updates_getState); _serializers.insert(mtpc_updates_getDifference, _serialize_updates_getDifference); _serializers.insert(mtpc_updates_getChannelDifference, _serialize_updates_getChannelDifference); diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h index cf53b5277..2e3909c79 100644 --- a/Telegram/SourceFiles/mtproto/mtpScheme.h +++ b/Telegram/SourceFiles/mtproto/mtpScheme.h @@ -240,6 +240,7 @@ enum { mtpc_inputMessagesFilterAudio = 0xcfc87522, mtpc_inputMessagesFilterAudioDocuments = 0x5afbf764, mtpc_inputMessagesFilterUrl = 0x7ef0dd87, + mtpc_inputMessagesFilterGif = 0xffc86587, mtpc_updateNewMessage = 0x1f2b0afd, mtpc_updateMessageID = 0x4e90bfd6, mtpc_updateDeleteMessages = 0xa20db0e5, @@ -280,6 +281,7 @@ enum { mtpc_updateNewStickerSet = 0x688a30aa, mtpc_updateStickerSetsOrder = 0xf0dfb451, mtpc_updateStickerSets = 0x43ae3dec, + mtpc_updateSavedGifs = 0x9375341e, mtpc_updates_state = 0xa56c2a3e, mtpc_updates_differenceEmpty = 0x5d75a138, mtpc_updates_difference = 0xf49ca0, @@ -296,7 +298,7 @@ enum { mtpc_photos_photo = 0x20212ca8, mtpc_upload_file = 0x96a18d5, mtpc_dcOption = 0x5d8c6cc, - mtpc_config = 0x6cb6e65e, + mtpc_config = 0x6bbc5f8, mtpc_nearestDc = 0x8e1a1775, mtpc_help_appUpdate = 0x8987f311, mtpc_help_noAppUpdate = 0xc45a6536, @@ -448,6 +450,8 @@ enum { mtpc_help_termsOfService = 0xf1ee3e90, mtpc_foundGif = 0xd579cccb, mtpc_messages_foundGifs = 0x450a1c0a, + mtpc_messages_savedGifsNotModified = 0xe8025ca2, + mtpc_messages_savedGifs = 0x2e0709a5, mtpc_invokeAfterMsg = 0xcb9f372d, mtpc_invokeAfterMsgs = 0x3dc4b4f0, mtpc_initConnection = 0x69796de9, @@ -559,6 +563,8 @@ enum { mtpc_messages_reorderStickerSets = 0x9fcfbc30, mtpc_messages_getDocumentByHash = 0x338e2464, mtpc_messages_searchGifs = 0xbf9a776b, + mtpc_messages_getSavedGifs = 0x83bf3d52, + mtpc_messages_saveGif = 0x327a30cb, mtpc_updates_getState = 0xedd4882a, mtpc_updates_getDifference = 0xa041495, mtpc_updates_getChannelDifference = 0xbb32d7c0, @@ -1229,6 +1235,9 @@ class MTPDfoundGif; class MTPmessages_foundGifs; class MTPDmessages_foundGifs; +class MTPmessages_savedGifs; +class MTPDmessages_savedGifs; + // Boxed types definitions typedef MTPBoxed MTPResPQ; @@ -1390,6 +1399,7 @@ typedef MTPBoxed MTPchannels_ChannelParticipant; typedef MTPBoxed MTPhelp_TermsOfService; typedef MTPBoxed MTPFoundGif; typedef MTPBoxed MTPmessages_FoundGifs; +typedef MTPBoxed MTPmessages_SavedGifs; // Type classes definitions @@ -5198,6 +5208,7 @@ private: friend MTPmessagesFilter MTP_inputMessagesFilterAudio(); friend MTPmessagesFilter MTP_inputMessagesFilterAudioDocuments(); friend MTPmessagesFilter MTP_inputMessagesFilterUrl(); + friend MTPmessagesFilter MTP_inputMessagesFilterGif(); mtpTypeId _type; }; @@ -5768,6 +5779,7 @@ private: friend MTPupdate MTP_updateNewStickerSet(const MTPmessages_StickerSet &_stickerset); friend MTPupdate MTP_updateStickerSetsOrder(const MTPVector &_order); friend MTPupdate MTP_updateStickerSets(); + friend MTPupdate MTP_updateSavedGifs(); mtpTypeId _type; }; @@ -6148,7 +6160,7 @@ public: private: explicit MTPconfig(MTPDconfig *_data); - friend MTPconfig MTP_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, const MTPVector &_disabled_features); + friend MTPconfig MTP_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, const MTPVector &_disabled_features); }; typedef MTPBoxed MTPConfig; @@ -9013,6 +9025,44 @@ private: }; typedef MTPBoxed MTPmessages_FoundGifs; +class MTPmessages_savedGifs : private mtpDataOwner { +public: + MTPmessages_savedGifs() : mtpDataOwner(0), _type(0) { + } + MTPmessages_savedGifs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : mtpDataOwner(0), _type(0) { + read(from, end, cons); + } + + MTPDmessages_savedGifs &_messages_savedGifs() { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_messages_savedGifs) throw mtpErrorWrongTypeId(_type, mtpc_messages_savedGifs); + split(); + return *(MTPDmessages_savedGifs*)data; + } + const MTPDmessages_savedGifs &c_messages_savedGifs() const { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_messages_savedGifs) throw mtpErrorWrongTypeId(_type, mtpc_messages_savedGifs); + return *(const MTPDmessages_savedGifs*)data; + } + + uint32 innerLength() const; + mtpTypeId type() const; + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons); + void write(mtpBuffer &to) const; + + typedef void ResponseType; + +private: + explicit MTPmessages_savedGifs(mtpTypeId type); + explicit MTPmessages_savedGifs(MTPDmessages_savedGifs *_data); + + friend MTPmessages_savedGifs MTP_messages_savedGifsNotModified(); + friend MTPmessages_savedGifs MTP_messages_savedGifs(MTPint _hash, const MTPVector &_gifs); + + mtpTypeId _type; +}; +typedef MTPBoxed MTPmessages_SavedGifs; + // Type constructors with data class MTPDresPQ : public mtpDataImpl { @@ -11635,7 +11685,7 @@ class MTPDconfig : public mtpDataImpl { public: MTPDconfig() { } - MTPDconfig(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, const MTPVector &_disabled_features) : vdate(_date), vexpires(_expires), vtest_mode(_test_mode), vthis_dc(_this_dc), vdc_options(_dc_options), vchat_size_max(_chat_size_max), vmegagroup_size_max(_megagroup_size_max), vforwarded_count_max(_forwarded_count_max), vonline_update_period_ms(_online_update_period_ms), voffline_blur_timeout_ms(_offline_blur_timeout_ms), voffline_idle_timeout_ms(_offline_idle_timeout_ms), vonline_cloud_timeout_ms(_online_cloud_timeout_ms), vnotify_cloud_delay_ms(_notify_cloud_delay_ms), vnotify_default_delay_ms(_notify_default_delay_ms), vchat_big_size(_chat_big_size), vpush_chat_period_ms(_push_chat_period_ms), vpush_chat_limit(_push_chat_limit), vdisabled_features(_disabled_features) { + MTPDconfig(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, const MTPVector &_disabled_features) : vdate(_date), vexpires(_expires), vtest_mode(_test_mode), vthis_dc(_this_dc), vdc_options(_dc_options), vchat_size_max(_chat_size_max), vmegagroup_size_max(_megagroup_size_max), vforwarded_count_max(_forwarded_count_max), vonline_update_period_ms(_online_update_period_ms), voffline_blur_timeout_ms(_offline_blur_timeout_ms), voffline_idle_timeout_ms(_offline_idle_timeout_ms), vonline_cloud_timeout_ms(_online_cloud_timeout_ms), vnotify_cloud_delay_ms(_notify_cloud_delay_ms), vnotify_default_delay_ms(_notify_default_delay_ms), vchat_big_size(_chat_big_size), vpush_chat_period_ms(_push_chat_period_ms), vpush_chat_limit(_push_chat_limit), vsaved_gifs_limit(_saved_gifs_limit), vdisabled_features(_disabled_features) { } MTPint vdate; @@ -11655,6 +11705,7 @@ public: MTPint vchat_big_size; MTPint vpush_chat_period_ms; MTPint vpush_chat_limit; + MTPint vsaved_gifs_limit; MTPVector vdisabled_features; }; @@ -13048,6 +13099,17 @@ public: MTPVector vresults; }; +class MTPDmessages_savedGifs : public mtpDataImpl { +public: + MTPDmessages_savedGifs() { + } + MTPDmessages_savedGifs(MTPint _hash, const MTPVector &_gifs) : vhash(_hash), vgifs(_gifs) { + } + + MTPint vhash; + MTPVector vgifs; +}; + // RPC methods class MTPreq_pq { // RPC method 'req_pq' @@ -18085,6 +18147,87 @@ public: } }; +class MTPmessages_getSavedGifs { // RPC method 'messages.getSavedGifs' +public: + MTPint vhash; + + MTPmessages_getSavedGifs() { + } + MTPmessages_getSavedGifs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getSavedGifs) { + read(from, end, cons); + } + MTPmessages_getSavedGifs(MTPint _hash) : vhash(_hash) { + } + + uint32 innerLength() const { + return vhash.innerLength(); + } + mtpTypeId type() const { + return mtpc_messages_getSavedGifs; + } + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getSavedGifs) { + vhash.read(from, end); + } + void write(mtpBuffer &to) const { + vhash.write(to); + } + + typedef MTPmessages_SavedGifs ResponseType; +}; +class MTPmessages_GetSavedGifs : public MTPBoxed { +public: + MTPmessages_GetSavedGifs() { + } + MTPmessages_GetSavedGifs(const MTPmessages_getSavedGifs &v) : MTPBoxed(v) { + } + MTPmessages_GetSavedGifs(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed(from, end, cons) { + } + MTPmessages_GetSavedGifs(MTPint _hash) : MTPBoxed(MTPmessages_getSavedGifs(_hash)) { + } +}; + +class MTPmessages_saveGif { // RPC method 'messages.saveGif' +public: + MTPInputDocument vid; + MTPBool vunsave; + + MTPmessages_saveGif() { + } + MTPmessages_saveGif(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_saveGif) { + read(from, end, cons); + } + MTPmessages_saveGif(const MTPInputDocument &_id, MTPBool _unsave) : vid(_id), vunsave(_unsave) { + } + + uint32 innerLength() const { + return vid.innerLength() + vunsave.innerLength(); + } + mtpTypeId type() const { + return mtpc_messages_saveGif; + } + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_saveGif) { + vid.read(from, end); + vunsave.read(from, end); + } + void write(mtpBuffer &to) const { + vid.write(to); + vunsave.write(to); + } + + typedef MTPBool ResponseType; +}; +class MTPmessages_SaveGif : public MTPBoxed { +public: + MTPmessages_SaveGif() { + } + MTPmessages_SaveGif(const MTPmessages_saveGif &v) : MTPBoxed(v) { + } + MTPmessages_SaveGif(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed(from, end, cons) { + } + MTPmessages_SaveGif(const MTPInputDocument &_id, MTPBool _unsave) : MTPBoxed(MTPmessages_saveGif(_id, _unsave)) { + } +}; + class MTPupdates_getState { // RPC method 'updates.getState' public: MTPupdates_getState() { @@ -24951,6 +25094,7 @@ inline void MTPmessagesFilter::read(const mtpPrime *&from, const mtpPrime *end, case mtpc_inputMessagesFilterAudio: _type = cons; break; case mtpc_inputMessagesFilterAudioDocuments: _type = cons; break; case mtpc_inputMessagesFilterUrl: _type = cons; break; + case mtpc_inputMessagesFilterGif: _type = cons; break; default: throw mtpErrorUnexpected(cons, "MTPmessagesFilter"); } } @@ -24969,6 +25113,7 @@ inline MTPmessagesFilter::MTPmessagesFilter(mtpTypeId type) : _type(type) { case mtpc_inputMessagesFilterAudio: break; case mtpc_inputMessagesFilterAudioDocuments: break; case mtpc_inputMessagesFilterUrl: break; + case mtpc_inputMessagesFilterGif: break; default: throw mtpErrorBadTypeId(type, "MTPmessagesFilter"); } } @@ -24999,6 +25144,9 @@ inline MTPmessagesFilter MTP_inputMessagesFilterAudioDocuments() { inline MTPmessagesFilter MTP_inputMessagesFilterUrl() { return MTPmessagesFilter(mtpc_inputMessagesFilterUrl); } +inline MTPmessagesFilter MTP_inputMessagesFilterGif() { + return MTPmessagesFilter(mtpc_inputMessagesFilterGif); +} inline uint32 MTPupdate::innerLength() const { switch (_type) { @@ -25426,6 +25574,7 @@ inline void MTPupdate::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeI v.vorder.read(from, end); } break; case mtpc_updateStickerSets: _type = cons; break; + case mtpc_updateSavedGifs: _type = cons; break; default: throw mtpErrorUnexpected(cons, "MTPupdate"); } } @@ -25693,6 +25842,7 @@ inline MTPupdate::MTPupdate(mtpTypeId type) : mtpDataOwner(0), _type(type) { case mtpc_updateNewStickerSet: setData(new MTPDupdateNewStickerSet()); break; case mtpc_updateStickerSetsOrder: setData(new MTPDupdateStickerSetsOrder()); break; case mtpc_updateStickerSets: break; + case mtpc_updateSavedGifs: break; default: throw mtpErrorBadTypeId(type, "MTPupdate"); } } @@ -25894,6 +26044,9 @@ inline MTPupdate MTP_updateStickerSetsOrder(const MTPVector &_order) { inline MTPupdate MTP_updateStickerSets() { return MTPupdate(mtpc_updateStickerSets); } +inline MTPupdate MTP_updateSavedGifs() { + return MTPupdate(mtpc_updateSavedGifs); +} inline MTPupdates_state::MTPupdates_state() : mtpDataOwner(new MTPDupdates_state()) { } @@ -26419,7 +26572,7 @@ inline MTPconfig::MTPconfig() : mtpDataOwner(new MTPDconfig()) { inline uint32 MTPconfig::innerLength() const { const MTPDconfig &v(c_config()); - return v.vdate.innerLength() + v.vexpires.innerLength() + v.vtest_mode.innerLength() + v.vthis_dc.innerLength() + v.vdc_options.innerLength() + v.vchat_size_max.innerLength() + v.vmegagroup_size_max.innerLength() + v.vforwarded_count_max.innerLength() + v.vonline_update_period_ms.innerLength() + v.voffline_blur_timeout_ms.innerLength() + v.voffline_idle_timeout_ms.innerLength() + v.vonline_cloud_timeout_ms.innerLength() + v.vnotify_cloud_delay_ms.innerLength() + v.vnotify_default_delay_ms.innerLength() + v.vchat_big_size.innerLength() + v.vpush_chat_period_ms.innerLength() + v.vpush_chat_limit.innerLength() + v.vdisabled_features.innerLength(); + return v.vdate.innerLength() + v.vexpires.innerLength() + v.vtest_mode.innerLength() + v.vthis_dc.innerLength() + v.vdc_options.innerLength() + v.vchat_size_max.innerLength() + v.vmegagroup_size_max.innerLength() + v.vforwarded_count_max.innerLength() + v.vonline_update_period_ms.innerLength() + v.voffline_blur_timeout_ms.innerLength() + v.voffline_idle_timeout_ms.innerLength() + v.vonline_cloud_timeout_ms.innerLength() + v.vnotify_cloud_delay_ms.innerLength() + v.vnotify_default_delay_ms.innerLength() + v.vchat_big_size.innerLength() + v.vpush_chat_period_ms.innerLength() + v.vpush_chat_limit.innerLength() + v.vsaved_gifs_limit.innerLength() + v.vdisabled_features.innerLength(); } inline mtpTypeId MTPconfig::type() const { return mtpc_config; @@ -26446,6 +26599,7 @@ inline void MTPconfig::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeI v.vchat_big_size.read(from, end); v.vpush_chat_period_ms.read(from, end); v.vpush_chat_limit.read(from, end); + v.vsaved_gifs_limit.read(from, end); v.vdisabled_features.read(from, end); } inline void MTPconfig::write(mtpBuffer &to) const { @@ -26467,12 +26621,13 @@ inline void MTPconfig::write(mtpBuffer &to) const { v.vchat_big_size.write(to); v.vpush_chat_period_ms.write(to); v.vpush_chat_limit.write(to); + v.vsaved_gifs_limit.write(to); v.vdisabled_features.write(to); } inline MTPconfig::MTPconfig(MTPDconfig *_data) : mtpDataOwner(_data) { } -inline MTPconfig MTP_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, const MTPVector &_disabled_features) { - return MTPconfig(new MTPDconfig(_date, _expires, _test_mode, _this_dc, _dc_options, _chat_size_max, _megagroup_size_max, _forwarded_count_max, _online_update_period_ms, _offline_blur_timeout_ms, _offline_idle_timeout_ms, _online_cloud_timeout_ms, _notify_cloud_delay_ms, _notify_default_delay_ms, _chat_big_size, _push_chat_period_ms, _push_chat_limit, _disabled_features)); +inline MTPconfig MTP_config(MTPint _date, MTPint _expires, MTPBool _test_mode, MTPint _this_dc, const MTPVector &_dc_options, MTPint _chat_size_max, MTPint _megagroup_size_max, MTPint _forwarded_count_max, MTPint _online_update_period_ms, MTPint _offline_blur_timeout_ms, MTPint _offline_idle_timeout_ms, MTPint _online_cloud_timeout_ms, MTPint _notify_cloud_delay_ms, MTPint _notify_default_delay_ms, MTPint _chat_big_size, MTPint _push_chat_period_ms, MTPint _push_chat_limit, MTPint _saved_gifs_limit, const MTPVector &_disabled_features) { + return MTPconfig(new MTPDconfig(_date, _expires, _test_mode, _this_dc, _dc_options, _chat_size_max, _megagroup_size_max, _forwarded_count_max, _online_update_period_ms, _offline_blur_timeout_ms, _offline_idle_timeout_ms, _online_cloud_timeout_ms, _notify_cloud_delay_ms, _notify_default_delay_ms, _chat_big_size, _push_chat_period_ms, _push_chat_limit, _saved_gifs_limit, _disabled_features)); } inline MTPnearestDc::MTPnearestDc() : mtpDataOwner(new MTPDnearestDc()) { @@ -30193,6 +30348,57 @@ inline MTPmessages_foundGifs MTP_messages_foundGifs(MTPint _next_offset, const M return MTPmessages_foundGifs(new MTPDmessages_foundGifs(_next_offset, _results)); } +inline uint32 MTPmessages_savedGifs::innerLength() const { + switch (_type) { + case mtpc_messages_savedGifs: { + const MTPDmessages_savedGifs &v(c_messages_savedGifs()); + return v.vhash.innerLength() + v.vgifs.innerLength(); + } + } + return 0; +} +inline mtpTypeId MTPmessages_savedGifs::type() const { + if (!_type) throw mtpErrorUninitialized(); + return _type; +} +inline void MTPmessages_savedGifs::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) { + if (cons != _type) setData(0); + switch (cons) { + case mtpc_messages_savedGifsNotModified: _type = cons; break; + case mtpc_messages_savedGifs: _type = cons; { + if (!data) setData(new MTPDmessages_savedGifs()); + MTPDmessages_savedGifs &v(_messages_savedGifs()); + v.vhash.read(from, end); + v.vgifs.read(from, end); + } break; + default: throw mtpErrorUnexpected(cons, "MTPmessages_savedGifs"); + } +} +inline void MTPmessages_savedGifs::write(mtpBuffer &to) const { + switch (_type) { + case mtpc_messages_savedGifs: { + const MTPDmessages_savedGifs &v(c_messages_savedGifs()); + v.vhash.write(to); + v.vgifs.write(to); + } break; + } +} +inline MTPmessages_savedGifs::MTPmessages_savedGifs(mtpTypeId type) : mtpDataOwner(0), _type(type) { + switch (type) { + case mtpc_messages_savedGifsNotModified: break; + case mtpc_messages_savedGifs: setData(new MTPDmessages_savedGifs()); break; + default: throw mtpErrorBadTypeId(type, "MTPmessages_savedGifs"); + } +} +inline MTPmessages_savedGifs::MTPmessages_savedGifs(MTPDmessages_savedGifs *_data) : mtpDataOwner(_data), _type(mtpc_messages_savedGifs) { +} +inline MTPmessages_savedGifs MTP_messages_savedGifsNotModified() { + return MTPmessages_savedGifs(mtpc_messages_savedGifsNotModified); +} +inline MTPmessages_savedGifs MTP_messages_savedGifs(MTPint _hash, const MTPVector &_gifs) { + return MTPmessages_savedGifs(new MTPDmessages_savedGifs(_hash, _gifs)); +} + // Human-readable text serialization #if (defined _DEBUG || defined _WITH_DEBUG) diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl index 441edfd1e..90ae62dd7 100644 --- a/Telegram/SourceFiles/mtproto/scheme.tl +++ b/Telegram/SourceFiles/mtproto/scheme.tl @@ -357,6 +357,7 @@ inputMessagesFilterDocument#9eddf188 = MessagesFilter; inputMessagesFilterAudio#cfc87522 = MessagesFilter; inputMessagesFilterAudioDocuments#5afbf764 = MessagesFilter; inputMessagesFilterUrl#7ef0dd87 = MessagesFilter; +inputMessagesFilterGif#ffc86587 = MessagesFilter; updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update; updateMessageID#4e90bfd6 id:int random_id:long = Update; @@ -398,6 +399,7 @@ updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool versio updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update; updateStickerSetsOrder#f0dfb451 order:Vector = Update; updateStickerSets#43ae3dec = Update; +updateSavedGifs#9375341e = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -422,7 +424,7 @@ upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File; dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true id:int ip_address:string port:int = DcOption; -config#6cb6e65e date:int expires:int test_mode:Bool this_dc:int dc_options:Vector chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int disabled_features:Vector = Config; +config#6bbc5f8 date:int expires:int test_mode:Bool this_dc:int dc_options:Vector chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int disabled_features:Vector = Config; nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; @@ -641,6 +643,9 @@ foundGif#d579cccb webpage:WebPage = FoundGif; messages.foundGifs#450a1c0a next_offset:int results:Vector = messages.FoundGifs; +messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs; +messages.savedGifs#2e0709a5 hash:int gifs:Vector = messages.SavedGifs; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -759,6 +764,8 @@ messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer of messages.reorderStickerSets#9fcfbc30 order:Vector = Bool; messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document; messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs; +messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs; +messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 7189372f9..d2889f7c6 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -345,7 +345,7 @@ void OverviewInner::moveToNextItem(MsgId &msgId, int32 &index, MsgId upTo, int32 } } -void OverviewInner::redrawItem(MsgId itemId, int32 itemIndex) { +void OverviewInner::repaintItem(MsgId itemId, int32 itemIndex) { fixItemIndex(itemIndex, itemId); if (itemIndex >= 0) { if (_type == OverviewPhotos || _type == OverviewVideos) { @@ -476,10 +476,10 @@ void OverviewInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton but if (button != Qt::LeftButton) return; if (textlnkDown() != textlnkOver()) { - redrawItem(App::pressedLinkItem()); + repaintItem(App::pressedLinkItem()); textlnkDown(textlnkOver()); App::pressedLinkItem(App::hoveredLinkItem()); - redrawItem(App::pressedLinkItem()); + repaintItem(App::pressedLinkItem()); } _dragAction = NoDrag; @@ -508,12 +508,12 @@ void OverviewInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton but uint32 selStatus = (_dragSymbol << 16) | _dragSymbol; if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) { if (!_selected.isEmpty()) { - redrawItem(_selected.cbegin().key(), -1); + repaintItem(_selected.cbegin().key(), -1); _selected.clear(); } _selected.insert(_dragItem, selStatus); _dragAction = Selecting; - redrawItem(_dragItem, _dragItemIndex); + repaintItem(_dragItem, _dragItemIndex); _overview->updateTopBarSelection(); } else { _dragAction = PrepareSelect; @@ -552,7 +552,7 @@ void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton bu } } if (textlnkDown()) { - redrawItem(App::pressedLinkItem()); + repaintItem(App::pressedLinkItem()); textlnkDown(TextLinkPtr()); App::pressedLinkItem(0); if (!textlnkOver() && _cursor != style::cur_default) { @@ -577,16 +577,16 @@ void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton bu } else { _selected.erase(i); } - redrawItem(_dragItem, _dragItemIndex); + repaintItem(_dragItem, _dragItemIndex); } else if (_dragAction == PrepareDrag && !needClick && !_dragWasInactive && button != Qt::RightButton) { SelectedItems::iterator i = _selected.find(_dragItem); if (i != _selected.cend() && i.value() == FullSelection) { _selected.erase(i); - redrawItem(_dragItem, _dragItemIndex); + repaintItem(_dragItem, _dragItemIndex); } else if (i == _selected.cend() && itemMsgId(_dragItem) > 0 && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { if (_selected.size() < MaxSelectedItems) { _selected.insert(_dragItem, FullSelection); - redrawItem(_dragItem, _dragItemIndex); + repaintItem(_dragItem, _dragItemIndex); } } else { _selected.clear(); @@ -994,7 +994,7 @@ void OverviewInner::onUpdateSelected() { fixItemIndex(itemIndex, itemId); if (itemIndex >= 0) { _items.at(itemIndex)->linkOut(textlnkOver()); - redrawItem(itemId, itemIndex); + repaintItem(itemId, itemIndex); } } } @@ -1004,7 +1004,7 @@ void OverviewInner::onUpdateSelected() { if (textlnkOver()) { if (item && index >= 0) { _items.at(index)->linkOver(textlnkOver()); - redrawItem(complexMsgId(item), index); + repaintItem(complexMsgId(item), index); } } } else { @@ -1012,8 +1012,8 @@ void OverviewInner::onUpdateSelected() { } if (_mousedItem != oldMousedItem) { lnkChanged = true; - if (oldMousedItem) redrawItem(oldMousedItem, oldMousedItemIndex); - if (item) redrawItem(item); + if (oldMousedItem) repaintItem(oldMousedItem, oldMousedItemIndex); + if (item) repaintItem(item); QToolTip::hideText(); } if (_cursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) { @@ -1199,11 +1199,11 @@ void OverviewInner::enterEvent(QEvent *e) { void OverviewInner::leaveEvent(QEvent *e) { if (_selectedMsgId) { - redrawItem(_selectedMsgId, -1); + repaintItem(_selectedMsgId, -1); _selectedMsgId = 0; } if (textlnkOver()) { - redrawItem(App::hoveredLinkItem()); + repaintItem(App::hoveredLinkItem()); textlnkOver(TextLinkPtr()); App::hoveredLinkItem(0); if (!textlnkDown() && _cursor != style::cur_default) { @@ -1223,8 +1223,8 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (_menu) { _menu->deleteLater(); _menu = 0; - redrawItem(App::contextItem()); - if (_selectedMsgId) redrawItem(_selectedMsgId, -1); + repaintItem(App::contextItem()); + if (_selectedMsgId) repaintItem(_selectedMsgId, -1); } if (e->reason() == QContextMenuEvent::Mouse) { dragActionUpdate(e->globalPos()); @@ -1301,8 +1301,8 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } App::contextItem(App::hoveredLinkItem()); - redrawItem(App::contextItem()); - if (_selectedMsgId) redrawItem(_selectedMsgId, -1); + repaintItem(App::contextItem()); + if (_selectedMsgId) repaintItem(_selectedMsgId, -1); } else if (!ignoreMousedItem && App::mousedItem() && App::mousedItem()->channelId() == itemChannel(_mousedItem) && App::mousedItem()->id == itemMsgId(_mousedItem)) { _menu = new PopupMenu(); if ((_contextMenuLnk && dynamic_cast(_contextMenuLnk.data()))) { @@ -1340,8 +1340,8 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } App::contextItem(App::mousedItem()); - redrawItem(App::contextItem()); - if (_selectedMsgId) redrawItem(_selectedMsgId, -1); + repaintItem(App::contextItem()); + if (_selectedMsgId) repaintItem(_selectedMsgId, -1); } if (_menu) { connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); @@ -1627,8 +1627,8 @@ void OverviewInner::onMenuDestroy(QObject *obj) { if (_menu == obj) { _menu = 0; dragActionUpdate(QCursor::pos()); - redrawItem(App::contextItem()); - if (_selectedMsgId) redrawItem(_selectedMsgId, -1); + repaintItem(App::contextItem()); + if (_selectedMsgId) repaintItem(_selectedMsgId, -1); } } @@ -1719,7 +1719,7 @@ void OverviewInner::mediaOverviewUpdated() { allGood = false; } HistoryItem *item = App::histItemById(itemChannel(msgid), itemMsgId(msgid)); - LayoutMediaItem *layout = getItemLayout(item); + LayoutMediaItem *layout = layoutPrepare(item); if (!layout) continue; setLayoutItem(index, layout, 0); @@ -1765,13 +1765,13 @@ void OverviewInner::mediaOverviewUpdated() { allGood = false; } HistoryItem *item = App::histItemById(itemChannel(msgid), itemMsgId(msgid)); - LayoutMediaItem *layout = getItemLayout(item); + LayoutMediaItem *layout = layoutPrepare(item); if (!layout) continue; if (withDates) { QDate date = item->date.date(); if (!index || (index > 0 && (dateEveryMonth ? (date.month() != prevDate.month() || date.year() != prevDate.year()) : (date != prevDate)))) { - top += setLayoutItem(index, getDateLayout(date, dateEveryMonth), top); + top += setLayoutItem(index, layoutPrepare(date, dateEveryMonth), top); ++index; prevDate = date; } @@ -1851,7 +1851,7 @@ void OverviewInner::itemRemoved(HistoryItem *item) { update(); } -void OverviewInner::redrawItem(const HistoryItem *msg) { +void OverviewInner::repaintItem(const HistoryItem *msg) { if (!msg) return; History *history = (msg->history() == _history) ? _history : (msg->history() == _migrated ? _migrated : 0); @@ -1921,7 +1921,7 @@ void OverviewInner::recountMargins() { } } -LayoutMediaItem *OverviewInner::getItemLayout(HistoryItem *item) { +LayoutMediaItem *OverviewInner::layoutPrepare(HistoryItem *item) { if (!item) return 0; LayoutItems::const_iterator i = _layoutItems.cend(); @@ -1963,7 +1963,7 @@ LayoutMediaItem *OverviewInner::getItemLayout(HistoryItem *item) { return (i == _layoutItems.cend()) ? 0 : i.value(); } -LayoutItem *OverviewInner::getDateLayout(const QDate &date, bool month) { +LayoutItem *OverviewInner::layoutPrepare(const QDate &date, bool month) { int32 key = date.year() * 100 + date.month(); if (!month) key = key * 100 + date.day(); LayoutDates::const_iterator i = _layoutDates.constFind(key); @@ -2288,9 +2288,9 @@ void OverviewWidget::changingMsgId(HistoryItem *row, MsgId newId) { } } -void OverviewWidget::ui_redrawHistoryItem(const HistoryItem *item) { +void OverviewWidget::ui_repaintHistoryItem(const HistoryItem *item) { if (peer() == item->history()->peer || migratePeer() == item->history()->peer) { - _inner.redrawItem(item); + _inner.repaintItem(item); } } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index b2a0ef107..62ea9d915 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -69,7 +69,7 @@ public: void mediaOverviewUpdated(); void changingMsgId(HistoryItem *row, MsgId newId); - void redrawItem(const HistoryItem *msg); + void repaintItem(const HistoryItem *msg); void itemRemoved(HistoryItem *item); void getSelectionState(int32 &selectedForForward, int32 &selectedForDelete) const; @@ -124,7 +124,7 @@ private: void updateDragSelection(MsgId dragSelFrom, int32 dragSelFromIndex, MsgId dragSelTo, int32 dragSelToIndex, bool dragSelecting); - void redrawItem(MsgId itemId, int32 itemIndex); + void repaintItem(MsgId itemId, int32 itemIndex); void touchResetSpeed(); void touchUpdateSpeed(); @@ -157,8 +157,8 @@ private: LayoutItems _layoutItems; typedef QMap LayoutDates; LayoutDates _layoutDates; - LayoutMediaItem *getItemLayout(HistoryItem *item); - LayoutItem *getDateLayout(const QDate &date, bool month); + LayoutMediaItem *layoutPrepare(HistoryItem *item); + LayoutItem *layoutPrepare(const QDate &date, bool month); int32 setLayoutItem(int32 index, LayoutItem *item, int32 top); FlatInput _search; @@ -299,7 +299,7 @@ public: resizeEvent(0); } - void ui_redrawHistoryItem(const HistoryItem *item); + void ui_repaintHistoryItem(const HistoryItem *item); void notify_historyItemLayoutChanged(const HistoryItem *item); diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 14eb6d2b9..c17b1c0e1 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -1832,7 +1832,9 @@ int32 ProfileWidget::lastScrollTop() const { void ProfileWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) { if (App::app()) App::app()->mtpPause(); -// App::stopGifItems(); + if (!cAutoPlayGif()) { + App::stopGifItems(); + } (back ? _cacheOver : _cacheUnder) = bgAnimCache; (back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache; diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index a3f59cff3..6fa99541b 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -108,15 +108,17 @@ RecentEmojiPack gRecentEmojis; RecentEmojisPreload gRecentEmojisPreload; EmojiColorVariants gEmojiVariants; -int32 gStickersHash = 0; - RecentStickerPreload gRecentStickersPreload; RecentStickerPack gRecentStickers; StickerSets gStickerSets; StickerSetsOrder gStickerSetsOrder; - uint64 gLastStickersUpdate = 0; +SavedGifs gSavedGifs; +uint64 gLastSavedGifsUpdate = 0; +bool gShowingSavedGifs = false; +int32 gSavedGifsLimit = 100; + RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags; bool gPasswordRecovered = false; @@ -174,6 +176,7 @@ ReportSpamStatuses gReportSpamStatuses; int32 gAutoDownloadPhoto = 0; // all auto download int32 gAutoDownloadAudio = 0; int32 gAutoDownloadGif = 0; +bool gAutoPlayGif = true; void settingsParseArgs(int argc, char *argv[]) { #ifdef Q_OS_MAC diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 6fc51ac85..a5aac46f1 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -197,7 +197,6 @@ RecentEmojiPack &cGetRecentEmojis(); class DocumentData; typedef QVector StickerPack; -DeclareSetting(int32, StickersHash); typedef QList > RecentStickerPackOld; typedef QVector > RecentStickerPreload; @@ -207,8 +206,6 @@ DeclareRefSetting(RecentStickerPack, RecentStickers); RecentStickerPack &cGetRecentStickers(); -DeclareSetting(uint64, LastStickersUpdate); - static const uint64 DefaultStickerSetId = 0; // for backward compatibility static const uint64 CustomStickerSetId = 0xFFFFFFFFFFFFFFFFULL, RecentStickerSetId = 0xFFFFFFFFFFFFFFFEULL; static const uint64 NoneStickerSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel @@ -224,6 +221,13 @@ typedef QMap StickerSets; DeclareRefSetting(StickerSets, StickerSets); typedef QList StickerSetsOrder; DeclareRefSetting(StickerSetsOrder, StickerSetsOrder); +DeclareSetting(uint64, LastStickersUpdate); + +typedef QVector SavedGifs; +DeclareRefSetting(SavedGifs, SavedGifs); +DeclareSetting(uint64, LastSavedGifsUpdate); +DeclareSetting(bool, ShowingSavedGifs); +DeclareSetting(int32, SavedGifsLimit); typedef QList > RecentHashtagPack; DeclareSetting(RecentHashtagPack, RecentWriteHashtags); @@ -335,5 +339,6 @@ enum DBIAutoDownloadFlags { DeclareSetting(int32, AutoDownloadPhoto); DeclareSetting(int32, AutoDownloadAudio); DeclareSetting(int32, AutoDownloadGif); +DeclareSetting(bool, AutoPlayGif); void settingsParseArgs(int argc, char *argv[]); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index fc22d4f2d..e25e9fa31 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -578,6 +578,10 @@ void PhotoData::automaticLoad(const HistoryItem *item) { full->automaticLoad(item); } +void PhotoData::automaticLoadSettingsChanged() { + full->automaticLoadSettingsChanged(); +} + void PhotoData::download() { full->loadEvenCancelled(); notifyLayoutChanged(); @@ -1158,6 +1162,11 @@ void AudioData::automaticLoad(const HistoryItem *item) { } } +void AudioData::automaticLoadSettingsChanged() { + if (loaded() || status != FileReady || !saveToCache() || _loader != CancelledFileLoader) return; + _loader = 0; +} + void AudioData::performActionOnLoad() { if (_actionOnLoad == ActionOnLoadNone) return; @@ -1332,6 +1341,8 @@ void DocumentOpenLink::doOpen(DocumentData *data, ActionOnLoad action) { HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); + MTP::send(MTPmessages_SendMedia(MTP_int(0), item->history()->peer->input, MTP_int(0), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(data->id), MTP_long(data->access))), MTP_long(MTP::nonce()), MTPnullMarkup)); + bool playMusic = data->song() && audioPlayer() && item; bool playAnimation = data->isAnimation() && item && item->getMedia(); const FileLocation &location(data->location(true)); @@ -1556,18 +1567,27 @@ void DocumentData::automaticLoad(const HistoryItem *item) { if (saveToCache() && _loader != CancelledFileLoader) { if (type == StickerDocument) { save(QString(), _actionOnLoad, _actionOnLoadMsgId); - } else if (isAnimation() && item) { + } else if (isAnimation()) { bool loadFromCloud = false; - if (item->history()->peer->isUser()) { - loadFromCloud = !(cAutoDownloadGif() & dbiadNoPrivate); - } else { - loadFromCloud = !(cAutoDownloadGif() & dbiadNoGroups); + if (item) { + if (item->history()->peer->isUser()) { + loadFromCloud = !(cAutoDownloadGif() & dbiadNoPrivate); + } else { + loadFromCloud = !(cAutoDownloadGif() & dbiadNoGroups); + } + } else { // if load at least anywhere + loadFromCloud = !(cAutoDownloadGif() & dbiadNoPrivate) || !(cAutoDownloadGif() & dbiadNoGroups); } save(QString(), _actionOnLoad, _actionOnLoadMsgId, loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true); } } } +void DocumentData::automaticLoadSettingsChanged() { + if (loaded() || status != FileReady || !isAnimation() || !saveToCache() || _loader != CancelledFileLoader) return; + _loader = 0; +} + void DocumentData::performActionOnLoad() { if (_actionOnLoad == ActionOnLoadNone) return; diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index ba4f1a533..0efbce5d8 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -733,6 +733,7 @@ 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); + void automaticLoadSettingsChanged(); void download(); bool loaded() const; @@ -823,6 +824,8 @@ public: void automaticLoad(const HistoryItem *item) { } + void automaticLoadSettingsChanged() { + } bool loaded(bool check = false) const; bool loading() const; @@ -919,6 +922,7 @@ 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 automaticLoad(const HistoryItem *item); // auto load voice message + void automaticLoadSettingsChanged(); bool loaded(bool check = false) const; bool loading() const; @@ -1079,6 +1083,7 @@ public: void setattributes(const QVector &attributes); void automaticLoad(const HistoryItem *item); // auto load sticker or video + void automaticLoadSettingsChanged(); bool loaded(bool check = false) const; bool loading() const; diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 5e533830c..53579ab76 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -294,6 +294,9 @@ enum DataBlockId { dbiMaxMegaGroupCount = 0x32, dbiDownloadPath = 0x33, dbiAutoDownload = 0x34, + dbiSavedGifsLimit = 0x35, + dbiShowingSavedGifs = 0x36, + dbiAutoPlay = 0x37, dbiEncryptedWithSalt = 333, dbiEncrypted = 444, @@ -469,3 +472,69 @@ static int32 FullArcLength = 360 * 16; static int32 QuarterArcLength = (FullArcLength / 4); static int32 MinArcLength = (FullArcLength / 360); static int32 AlmostFullArcLength = (FullArcLength - MinArcLength); + +template +class Function { +public: + + virtual ReturnType call() = 0; + virtual ~Function() { + } + +}; + +template +class ObjectFunction : public Function { +public: + typedef ReturnType (Object::*Method)(); + + ObjectFunction(Object *obj, Method method) : _obj(obj), _method(method) { + } + + virtual ReturnType call() { + return (_obj->*_method)(); + } + +private: + Object *_obj; + Method _method; + +}; + +template +Function *func(Object *obj, typename ReturnType (Object::*method)()) { + return new ObjectFunction(obj, method); +} + +template +class Function1 { +public: + + virtual ReturnType call(ArgumentType1 arg1) = 0; + virtual ~Function1() { + } + +}; + +template +class ObjectFunction1 : public Function1 { +public: + typedef ReturnType (Object::*Method)(ArgumentType1); + + ObjectFunction1(Object *obj, Method method) : _obj(obj), _method(method) { + } + + virtual ReturnType call(ArgumentType1 arg1) { + return (_obj->*_method)(arg1); + } + +private: + Object *_obj; + Method _method; + +}; + +template +Function1 *func(Object *obj, ReturnType (Object::*method)(ArgumentType1)) { + return new ObjectFunction1(obj, method); +} diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index b75fdc693..bbe853042 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -647,8 +647,6 @@ void Window::sendServiceHistoryRequest() { } void Window::setupMain(bool anim, const MTPUser *self) { - Local::readStickers(); - QPixmap bg = anim ? grabInner() : QPixmap(); clearWidgets(); main = new MainWidget(this); @@ -797,9 +795,9 @@ void Window::showDocument(DocumentData *doc, HistoryItem *item) { _mediaView->setFocus(); } -void Window::ui_clipRedraw(ClipReader *reader) { +void Window::ui_clipRepaint(ClipReader *reader) { if (_mediaView && !_mediaView->isHidden()) { - _mediaView->ui_clipRedraw(reader); + _mediaView->ui_clipRepaint(reader); } } diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 04e99717e..86ac00dbf 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -237,7 +237,7 @@ public: return contentOverlapped(QRect(w->mapToGlobal(r.boundingRect().topLeft()), r.boundingRect().size())); } - void ui_clipRedraw(ClipReader *reader); + void ui_clipRepaint(ClipReader *reader); void ui_showLayer(LayeredWidget *box, ShowLayerOptions options); bool ui_isLayerShown(); bool ui_isMediaViewShown();