sticker packs done

This commit is contained in:
John Preston 2015-05-19 18:46:45 +03:00
parent 136fd5c8e1
commit 92858dc7d3
39 changed files with 2312 additions and 702 deletions

View File

@ -428,7 +428,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_emoji_category5" = "Activity";
"lng_emoji_category6" = "Travel & Places";
"lng_emoji_category7" = "Objects & Symbols";
"lng_emoji_category8" = "Stickers";
"lng_switch_stickers" = "Stickers";
"lng_switch_emoji" = "Emoji";
"lng_custom_stickers" = "Custom stickers";
"lng_stickers_remove_pack" = "Remove «{sticker_pack}»?";
"lng_stickers_add_pack" = "Add {count:_not_used_|# Sticker|# Stickers}";
"lng_stickers_share_pack" = "Share Stickers";
"lng_stickers_not_found" = "Sticker pack not found.";
"lng_stickers_copied" = "Sticker pack link copied to clipboard.";
"lng_in_dlg_photo" = "Photo";
"lng_in_dlg_video" = "Video";
@ -481,6 +490,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_context_save_video" = "Save Video As..";
"lng_context_open_audio" = "Open Audio";
"lng_context_save_audio" = "Save Audio As..";
"lng_context_pack_info" = "Pack Info";
"lng_context_open_file" = "Open File";
"lng_context_save_file" = "Save File As..";
"lng_context_forward_file" = "Forward File";

View File

@ -308,7 +308,8 @@ scrollDef: flatScroll {
width: 10px;
minHeight: 20px;
deltax: 3px;
deltay: 3px;
deltat: 3px;
deltab: 3px;
topsh: 2px;
bottomsh: 2px;
@ -983,12 +984,12 @@ btnAttachPhoto: iconedButton(btnAttachDocument) {
}
btnAttachEmoji: iconedButton(btnAttachDocument) {
overBgColor: white;
icon: sprite(311px, 221px, 20px, 20px);
icon: sprite(363px, 344px, 21px, 22px);
iconPos: point(6px, 13px);
downIcon: sprite(311px, 221px, 20px, 20px);
downIcon: sprite(363px, 344px, 21px, 22px);
downIconPos: point(6px, 13px);
width: 32px;
width: 33px;
}
replySkip: 51px;
@ -1020,7 +1021,8 @@ historyScroll: flatScroll(scrollDef) {
width: 12px;
deltax: 3px;
deltay: 3px;
deltat: 3px;
deltab: 3px;
topsh: 0px;
bottomsh: -1px;
@ -1452,19 +1454,62 @@ dpiFont2: linkFont;
dpiFont3: linkFont;
dpiFont4: linkFont;
emojiScroll: flatScroll(scrollDef) {
width: 5px;
deltax: 2px;
deltay: 1px;
newScroll: flatScroll(scrollDef) {
barColor: #3f729734;
bgColor: #214f751a;
barOverColor: #3f729734;
bgOverColor: #214f751a;
deltax: 5px;
width: 14px;
deltat: 6px;
deltab: 6px;
topsh: 0px;
bottomsh: 0px;
hiding: 0;
}
stickersMaxHeight: 340px;
stickersAddOrShare: 70px;
btnStickersAdd: flatButton(btnDefNext, btnDefBig) {
width: 180px;
height: 42px;
textTop: 9px;
overTextTop: 9px;
downTextTop: 10px;
font: font(17px);
overFont: font(17px);
bgColor: #15c23c;
overBgColor: #13a835;
downBgColor: #13a835;
}
btnStickersClose: iconedButton(notifyClose) {
iconPos: point(21px, 21px);
downIconPos: point(21px, 22px);
width: 52px;
height: 48px;
}
stickersWidth: 344px;
stickersPadding: 10px;
stickersSize: size(64px, 64px);
stickersScroll: flatScroll(newScroll) {
deltab: 76px;
}
emojiScroll: flatScroll(newScroll) {
deltat: 48px;
}
emojiRecent: sprite(0px, 196px, 21px, 22px);
emojiRecentOver: sprite(287px, 220px, 21px, 22px);
emojiRecentActive: sprite(287px, 242px, 21px, 22px);
emojiPeople: sprite(21px, 196px, 21px, 22px);
emojiPeopleOver: sprite(298px, 220px, 21px, 22px);
emojiPeopleActive: sprite(298px, 242px, 21px, 22px);
emojiPeopleOver: sprite(308px, 220px, 21px, 22px);
emojiPeopleActive: sprite(308px, 242px, 21px, 22px);
emojiNature: sprite(42px, 196px, 21px, 22px);
emojiNatureOver: sprite(245px, 264px, 21px, 22px);
emojiNatureActive: sprite(245px, 286px, 21px, 22px);
@ -1483,10 +1528,13 @@ emojiTravelActive: sprite(321px, 366px, 21px, 22px);
emojiObjects: sprite(147px, 196px, 21px, 22px);
emojiObjectsOver: sprite(342px, 344px, 21px, 22px);
emojiObjectsActive: sprite(342px, 366px, 21px, 22px);
emojiPanCategories: #f7f7f7;
rbEmoji: flatCheckbox {
textColor: transparent;
bgColor: transparent;
disColor: transparent;
bgColor: emojiPanCategories;
disColor: emojiPanCategories;
width: 36px;
height: 46px;
@ -1565,27 +1613,33 @@ rbEmojiObjects: flatCheckbox(rbEmoji) {
disImageRect: emojiObjects;
chkDisImageRect: emojiObjectsActive;
}
emojiPanPadding: margins(5px, 0px, 0px, 5px);
emojiPanPadding: 10px;
emojiPanSize: size(39px, 35px);
emojiPanFullSize: size(300px, 321px);
emojiPanDuration: 200;
emojiPanHover: #f0f0f0;
emojiPanHover: #f0f4f7;
emojiPanRound: 2px;
emojiPanHeader: 25px;
emojiPanHeader: 42px;
emojiPanHeaderFont: font(fsize semibold);
emojiPanHeaderColor: #999;
emojiPanHeaderLeft: 5px;
emojiPanHeaderTop: 5px;
emojiPanHeaderBg: #fffd;
emojiPanHeaderLeft: 17px;
emojiPanHeaderTop: 12px;
emojiPanHeaderBg: #fffffff2;
emojiColorsPadding: 5px;
emojiColorsSep: 1px;
emojiColorsSepColor: #d5d5d5;
toStickersImg: sprite();
toEmojiImg: sprite();
emojiSwitchSkip: 27px;
emojiSwitchImgSkip: 21px;
emojiSwitchStickers: sprite(318px, 328px, 8px, 12px);
emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px);
emojiSwitchColor: #42a8db;
stickerPanSize: size(55px, 55px);
stickerPanRound: 3px;
stickerPanPadding: 2px;
stickerPanPadding: 11px;
stickerPanDelete: sprite(123px, 132px, 12px, 12px);
stickerPanDeleteOpacity: 0.5;

View File

@ -173,7 +173,8 @@ flatScroll {
width: number;
minHeight: number;
deltax: number;
deltay: number;
deltat: number;
deltab: number;
topsh: number;
bottomsh: number;

View File

@ -34,7 +34,6 @@ ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
}
void ApiWrap::init() {
App::initMedia();
}
void ApiWrap::itemRemoved(HistoryItem *item) {

View File

@ -633,7 +633,7 @@ namespace App {
const MTPDphotoSize &d(size.c_photoSize());
if (d.vlocation.type() == mtpc_fileLocation) {
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, d.vsize.v);
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), d.vsize.v);
}
} break;
case mtpc_photoCachedSize: {
@ -642,17 +642,43 @@ namespace App {
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
const string &s(d.vbytes.c_string().v);
QByteArray bytes(s.data(), s.size());
return ImagePtr(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v, bytes);
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v), bytes);
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
const string &s(d.vbytes.c_string().v);
QByteArray bytes(s.data(), s.size());
return ImagePtr(d.vw.v, d.vh.v, 0, 0, 0, 0, bytes);
return ImagePtr(StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0), bytes);
}
} break;
}
return ImagePtr();
}
StorageImageLocation imageLocation(const MTPPhotoSize &size) {
switch (size.type()) {
case mtpc_photoSize: {
const MTPDphotoSize &d(size.c_photoSize());
if (d.vlocation.type() == mtpc_fileLocation) {
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
}
} break;
case mtpc_photoCachedSize: {
const MTPDphotoCachedSize &d(size.c_photoCachedSize());
if (d.vlocation.type() == mtpc_fileLocation) {
const MTPDfileLocation &l(d.vlocation.c_fileLocation());
const string &s(d.vbytes.c_string().v);
QByteArray bytes(s.data(), s.size());
return StorageImageLocation(d.vw.v, d.vh.v, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
} else if (d.vlocation.type() == mtpc_fileLocationUnavailable) {
const string &s(d.vbytes.c_string().v);
QByteArray bytes(s.data(), s.size());
return StorageImageLocation(d.vw.v, d.vh.v, 0, 0, 0, 0);
}
} break;
}
return StorageImageLocation();
}
void feedWereRead(const QVector<MTPint> &msgsIds) {
for (QVector<MTPint>::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) {
MsgsData::const_iterator j = msgsData.constFind(i->v);
@ -874,7 +900,7 @@ namespace App {
switch (document.type()) {
case mtpc_document: {
const MTPDdocument &d(document.c_document());
return App::document(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v);
return App::documentSet(d.vid.v, 0, d.vaccess_hash.v, d.vdate.v, d.vattributes.c_vector().v, qs(d.vmime_type), ImagePtr(thumb, "JPG"), d.vdc_id.v, d.vsize.v, StorageImageLocation());
} break;
case mtpc_documentEmpty: return App::document(document.c_documentEmpty().vid.v);
}
@ -887,14 +913,14 @@ namespace App {
return feedDocument(document.c_document(), convert);
} break;
case mtpc_documentEmpty: {
return App::document(document.c_documentEmpty().vid.v, convert);
return App::documentSet(document.c_documentEmpty().vid.v, convert, 0, 0, QVector<MTPDocumentAttribute>(), QString(), ImagePtr(), 0, 0, StorageImageLocation());
} break;
}
return App::document(0);
}
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert) {
return App::document(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v);
return App::documentSet(document.vid.v, convert, document.vaccess_hash.v, document.vdate.v, document.vattributes.c_vector().v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v, App::imageLocation(document.vthumb));
}
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) {
@ -1128,7 +1154,15 @@ namespace App {
return result;
}
DocumentData *document(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) {
DocumentData *document(const DocumentId &document) {
DocumentsData::const_iterator i = documentsData.constFind(document);
if (i == documentsData.cend()) {
i = documentsData.insert(document, new DocumentData(document));
}
return i.value();
}
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation) {
if (convert) {
if (convert->id != document) {
DocumentsData::iterator i = documentsData.find(convert->id);
@ -1146,12 +1180,28 @@ namespace App {
convert->thumb = thumb;
convert->dc = dc;
convert->size = size;
} else if (convert->thumb->isNull() && !thumb->isNull()) {
convert->thumb = thumb;
} else {
if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) {
convert->thumb = thumb;
}
if (convert->sticker && !attributes.isEmpty() && (convert->sticker->alt.isEmpty() || convert->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
if (i->type() == mtpc_documentAttributeSticker) {
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
if (d.valt.c_string().v.length() > 0) {
convert->sticker->alt = qs(d.valt);
convert->sticker->set = d.vstickerset;
}
}
}
}
}
if (convert->sticker && !convert->sticker->loc.dc && thumbLocation.dc) {
convert->sticker->loc = thumbLocation;
}
if (convert->location.check()) {
Local::writeFileLocation(mediaKey(mtpc_inputDocumentFileLocation, convert->dc, convert->id), convert->location);
Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), convert->location);
}
}
DocumentsData::const_iterator i = documentsData.constFind(document);
@ -1161,6 +1211,7 @@ namespace App {
result = convert;
} else {
result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size);
if (result->sticker) result->sticker->loc = thumbLocation;
}
documentsData.insert(document, result);
} else {
@ -1175,19 +1226,23 @@ namespace App {
result->dc = dc;
result->size = size;
} else {
if (result->thumb->isNull() && !thumb->isNull()) {
if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) {
result->thumb = thumb;
}
if (result->sticker && result->sticker->alt.isEmpty()) {
if (result->sticker && !attributes.isEmpty() && (result->sticker->alt.isEmpty() || result->sticker->set.type() == mtpc_inputStickerSetEmpty)) {
for (QVector<MTPDocumentAttribute>::const_iterator i = attributes.cbegin(), e = attributes.cend(); i != e; ++i) {
if (i->type() == mtpc_documentAttributeSticker) {
const MTPDdocumentAttributeSticker &d(i->c_documentAttributeSticker());
if (d.valt.c_string().v.length() > 0) {
result->sticker->alt = qs(d.valt);
result->sticker->set = d.vstickerset;
}
}
}
}
if (result->sticker && !result->sticker->loc.dc && thumbLocation.dc) {
result->sticker->loc = thumbLocation;
}
}
}
}
@ -1464,8 +1519,9 @@ namespace App {
if (api()) api()->clearWebPageRequests();
cSetRecentStickers(RecentStickerPack());
cSetStickersHash(QByteArray());
cSetStickers(AllStickers());
cSetEmojiStickers(EmojiStickersMap());
cSetStickerSets(StickerSets());
cSetLastStickersUpdate(0);
::videoItems.clear();
::audioItems.clear();
::documentItems.clear();
@ -1854,6 +1910,12 @@ namespace App {
}
}
void stickersBox(const QString &name) {
if (App::main()) {
App::main()->stickersBox(MTP_inputStickerSetShortName(MTP_string(name)));
}
}
void openLocalUrl(const QString &url) {
if (App::main()) {
App::main()->openLocalUrl(url);

View File

@ -87,6 +87,7 @@ namespace App {
int32 maxMsgId();
ImagePtr image(const MTPPhotoSize &size);
StorageImageLocation imageLocation(const MTPPhotoSize &size);
PhotoData *feedPhoto(const MTPPhoto &photo, const PreparedPhotoThumbs &thumbs);
PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert = 0);
@ -117,7 +118,8 @@ namespace App {
PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr());
VideoData *video(const VideoId &video, VideoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &mime = QString(), int32 duration = 0, int32 dc = 0, int32 size = 0);
DocumentData *document(const DocumentId &document, DocumentData *convert = 0, const uint64 &access = 0, int32 date = 0, const QVector<MTPDocumentAttribute> &attributes = QVector<MTPDocumentAttribute>(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0);
DocumentData *document(const DocumentId &document);
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation);
WebPageData *webPage(const WebPageId &webPage, WebPageData *convert = 0, const QString &type = QString(), const QString &url = QString(), const QString &displayUrl = QString(), const QString &siteName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = 0, int32 duration = 0, const QString &author = QString(), int32 pendingTill = -2);
ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString());
void forgetMedia();
@ -200,6 +202,7 @@ namespace App {
void searchByHashtag(const QString &tag);
void openUserByName(const QString &username, bool toProfile = false);
void joinGroupByHash(const QString &hash);
void stickersBox(const QString &name);
void openLocalUrl(const QString &url);
void initBackground(int32 id = DefaultChatBackground, const QImage &p = QImage(), bool nowrite = false);

View File

@ -662,8 +662,8 @@ void Application::checkMapVersion() {
psRegisterCustomScheme();
if (Local::oldMapVersion()) {
QString versionFeatures;
if (DevChannel && Local::oldMapVersion() < 8012) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 New emojis support added\n\xe2\x80\x94 Emojis and stickers panel improved").replace('@', qsl("@") + QChar(0x200D));
if (DevChannel && Local::oldMapVersion() < 8014) {
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Added support for sticker packs\n\xe2\x80\x94 New emoji and sticker panel").replace('@', qsl("@") + QChar(0x200D));
} else if (!DevChannel && Local::oldMapVersion() < 8013) {
versionFeatures = lang(lng_new_version_text).trimmed();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 215 KiB

View File

@ -0,0 +1,275 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "lang.h"
#include "stickersetbox.h"
#include "mainwidget.h"
#include "window.h"
#include "settingswidget.h"
#include "boxes/confirmbox.h"
#include "localstorage.h"
StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) :
_loaded(false), _setId(0), _setAccess(0), _bottom(0),
_input(set), _installRequest(0) {
switch (set.type()) {
case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break;
case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break;
}
MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&StickerSetInner::gotSet), rpcFail(&StickerSetInner::failedSet));
cSetLastStickersUpdate(0);
App::main()->updateStickers();
}
void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
_pack.clear();
if (set.type() == mtpc_messages_stickerSet) {
const MTPDmessages_stickerSet &d(set.c_messages_stickerSet());
const QVector<MTPDocument> &v(d.vdocuments.c_vector().v);
_pack.reserve(v.size());
for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker) continue;
_pack.push_back(doc);
}
if (d.vset.type() == mtpc_stickerSet) {
const MTPDstickerSet &s(d.vset.c_stickerSet());
_setTitle = qs(s.vtitle);
_title = st::boxTitleFont->m.elidedText(_setTitle, Qt::ElideRight, width() - st::btnStickersClose.width - st::boxTitlePos.x());
_setShortName = qs(s.vshort_name);
_setId = s.vid.v;
_setAccess = s.vaccess_hash.v;
}
}
if (_pack.isEmpty() || _setShortName.isEmpty()) {
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
} else {
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
resize(st::stickersPadding + StickerPanPerRow * st::stickersSize.width(), rows * st::stickersSize.height() + st::stickersAddOrShare);
}
_loaded = true;
emit updateButtons();
}
bool StickerSetInner::failedSet(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_loaded = true;
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
return true;
}
void StickerSetInner::installDone(const MTPBool &result) {
StickerSets &sets(cRefStickerSets());
sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName)).value().stickers = _pack;
cSetStickersHash(QByteArray());
Local::writeStickers();
emit installed(_setId);
App::wnd()->hideLayer();
}
bool StickerSetInner::installFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_not_found), true), true);
return true;
}
void StickerSetInner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
if (_pack.isEmpty()) return;
int32 rows = _pack.size() / StickerPanPerRow + ((_pack.size() % StickerPanPerRow) ? 1 : 0);
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
for (int32 i = from; i < to; ++i) {
for (int32 j = 0; j < StickerPanPerRow; ++j) {
int32 index = i * StickerPanPerRow + j;
if (index >= _pack.size()) break;
DocumentData *doc = _pack.at(index);
QPoint pos(st::stickerPanPadding + j * st::stickersSize.width(), i * st::stickersSize.height());
bool goodThumb = !doc->thumb->isNull() && ((doc->thumb->width() >= 128) || (doc->thumb->height() >= 128));
if (goodThumb) {
doc->thumb->load();
} else {
bool already = !doc->already().isEmpty(), hasdata = !doc->data.isEmpty();
if (!doc->loader && doc->status != FileFailed && !already && !hasdata) {
doc->save(QString());
}
if (doc->sticker->img->isNull() && (already || hasdata)) {
if (already) {
doc->sticker->img = ImagePtr(doc->already());
} else {
doc->sticker->img = ImagePtr(doc->data);
}
}
}
float64 coef = qMin((st::stickersSize.width() - st::stickerPanRound * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::stickerPanRound * 2) / float64(doc->dimensions.height()));
if (coef > 1) coef = 1;
int32 w = qRound(coef * doc->dimensions.width()), h = qRound(coef * doc->dimensions.height());
if (w < 1) w = 1;
if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
if (goodThumb) {
p.drawPixmapLeft(ppos, width(), doc->thumb->pix(w, h));
} else if (!doc->sticker->img->isNull()) {
p.drawPixmapLeft(ppos, width(), doc->sticker->img->pix(w, h));
}
}
}
p.fillRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare, st::emojiPanHeaderBg->b);
}
void StickerSetInner::setScrollBottom(int32 bottom) {
if (bottom == _bottom) return;
QRegion upd = QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare);
_bottom = bottom;
upd += QRect(0, _bottom - st::stickersAddOrShare, width(), st::stickersAddOrShare);
repaint(upd);
}
bool StickerSetInner::loaded() const {
return _loaded && !_pack.isEmpty();
}
int32 StickerSetInner::notInstalled() const {
return (_loaded && (cStickerSets().constFind(_setId) == cStickerSets().cend())) ? _pack.size() : 0;
}
QString StickerSetInner::title() const {
return _loaded ? (_pack.isEmpty() ? lang(lng_attach_failed) : _title) : lang(lng_contacts_loading);
}
QString StickerSetInner::shortName() const {
return _setShortName;
}
void StickerSetInner::install() {
if (_installRequest) return;
_installRequest = MTP::send(MTPmessages_InstallStickerSet(_input), rpcDone(&StickerSetInner::installDone), rpcFail(&StickerSetInner::installFailed));
}
StickerSetInner::~StickerSetInner() {
}
StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::stickersScroll), _inner(set),
_close(this, st::btnStickersClose),
_addStickers(this, lng_stickers_add_pack(lt_count, 0), st::btnStickersAdd),
_shareStickers(this, lang(lng_stickers_share_pack), st::btnStickersAdd) {
resize(st::stickersWidth, height());
setMaxHeight(st::stickersMaxHeight);
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
init(&_inner, 0, st::boxFont->height + st::newGroupNamePadding.top() + st::newGroupNamePadding.bottom());
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_addStickers, SIGNAL(clicked()), this, SLOT(onAddStickers()));
connect(&_shareStickers, SIGNAL(clicked()), this, SLOT(onShareStickers()));
connect(&_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&_inner, SIGNAL(installed(uint64)), this, SIGNAL(installed(uint64)));
onStickersUpdated();
onScroll();
prepare();
}
void StickerSetBox::onStickersUpdated() {
showAll();
}
void StickerSetBox::onAddStickers() {
_inner.install();
}
void StickerSetBox::onShareStickers() {
QString url = qsl("https://telegram.me/addstickers/") + _inner.shortName();
DEBUG_LOG(("Setting text to clipboard from stickerset box: %1").arg(url));
QApplication::clipboard()->setText(url);
App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_copied), true), true);
}
void StickerSetBox::onUpdateButtons() {
if (!_close.isHidden()) showAll();
}
void StickerSetBox::onScroll() {
_inner.setScrollBottom(_scroll.scrollTop() + _scroll.height());
}
void StickerSetBox::hideAll() {
ScrollableBox::hideAll();
_close.hide();
_addStickers.hide();
_shareStickers.hide();
}
void StickerSetBox::showAll() {
ScrollableBox::showAll();
_close.show();
int32 cnt = _inner.notInstalled();
if (_inner.loaded()) {
if (_inner.notInstalled()) {
_addStickers.setText(lng_stickers_add_pack(lt_count, cnt));
_addStickers.show();
_addStickers.raise();
_shareStickers.hide();
} else {
_shareStickers.show();
_shareStickers.raise();
_addStickers.hide();
}
} else {
_addStickers.hide();
_shareStickers.hide();
}
update();
}
void StickerSetBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
paintTitle(p, _inner.title(), false);
}
void StickerSetBox::resizeEvent(QResizeEvent *e) {
ScrollableBox::resizeEvent(e);
_inner.resize(width(), _inner.height());
_close.moveToRight(0, 0, width());
_addStickers.move((width() - _addStickers.width()) / 2, height() - (st::stickersAddOrShare + _addStickers.height()) / 2);
_shareStickers.move((width() - _shareStickers.width()) / 2, height() - (st::stickersAddOrShare + _shareStickers.height()) / 2);
}

View File

@ -0,0 +1,100 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "abstractbox.h"
class StickerSetInner : public QWidget, public RPCSender {
Q_OBJECT
public:
StickerSetInner(const MTPInputStickerSet &set);
void init();
void paintEvent(QPaintEvent *e);
bool loaded() const;
int32 notInstalled() const;
QString title() const;
QString shortName() const;
void setScrollBottom(int32 bottom);
void install();
~StickerSetInner();
signals:
void updateButtons();
void installed(uint64 id);
private:
void gotSet(const MTPmessages_StickerSet &set);
bool failedSet(const RPCError &error);
void installDone(const MTPBool &result);
bool installFailed(const RPCError &error);
StickerPack _pack;
bool _loaded;
uint64 _setId, _setAccess;
QString _title, _setTitle, _setShortName;
int32 _bottom;
MTPInputStickerSet _input;
mtpRequestId _installRequest;
};
class StickerSetBox : public ScrollableBox, public RPCSender {
Q_OBJECT
public:
StickerSetBox(const MTPInputStickerSet &set);
void paintEvent(QPaintEvent *e);
void resizeEvent(QResizeEvent *e);
public slots:
void onStickersUpdated();
void onAddStickers();
void onShareStickers();
void onUpdateButtons();
void onScroll();
signals:
void installed(uint64 id);
protected:
void hideAll();
void showAll();
private:
StickerSetInner _inner;
IconedButton _close;
FlatButton _addStickers, _shareStickers;
};

View File

@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
static const int32 AppVersion = 8013;
static const wchar_t *AppVersionStr = L"0.8.13";
static const bool DevChannel = false;
static const int32 AppVersion = 8014;
static const wchar_t *AppVersionStr = L"0.8.14";
static const bool DevChannel = true;
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop";
@ -101,9 +101,10 @@ enum {
ZoomToScreenLevel = 1024, // just constant
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
EmojiPadPerRow = 7,
EmojiPadRowsPerPage = 6,
StickerPadPerRow = 3,
EmojiPanPerRow = 7,
EmojiPanRowsPerPage = 6,
StickerPanPerRow = 5,
StickerPanRowsPerPage = 4,
StickersUpdateTimeout = 3600000, // update not more than once in an hour
SearchPeopleLimit = 5,

View File

@ -1122,7 +1122,7 @@ void DialogsListWidget::saveRecentHashtags(const QString &text) {
}
}
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentStickers();
Local::readRecentHashtags();
recent = cRecentSearchHashtags();
}
found = true;

File diff suppressed because it is too large Load Diff

View File

@ -193,6 +193,8 @@ private:
};
static const int32 SwitcherSelected = (INT_MAX / 2);
class EmojiPanInner : public TWidget, public Animated {
Q_OBJECT
@ -218,7 +220,6 @@ public:
DBIEmojiTab currentTab(int yOffset) const;
void refreshStickers();
void refreshRecent();
void setScrollTop(int top);
@ -234,30 +235,27 @@ public slots:
signals:
void emojiSelected(EmojiPtr emoji);
void stickerSelected(DocumentData *sticker);
void selected(EmojiPtr emoji);
void switchToStickers();
void scrollToY(int y);
void disableScroll(bool dis);
private:
int countHeight();
int32 countHeight();
void selectEmoji(EmojiPtr emoji);
typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
EmojiAnimations _emojiAnimations;
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _animations;
int _top;
int _counts[emojiTabCount], _count;
int32 _top, _counts[emojiTabCount];
StickerPack _stickers;
QVector<bool> _isUserGen;
QVector<EmojiPtr> _emojis[emojiTabCount];
QVector<float64> _hovers[emojiTabCount + 1]; // + stickers hovers and stickers-x hovers
QVector<float64> _hovers[emojiTabCount];
float64 _stickerWidth;
int32 _esize, _stickerSize;
int32 _esize;
int32 _selected, _pressedSel, _pickerSel;
QPoint _lastMousePos;
@ -267,6 +265,74 @@ private:
EmojiColorPicker _picker;
QTimer _showPickerTimer;
float64 _switcherHover;
int32 _stickersWidth;
};
class StickerPanInner : public TWidget, public Animated {
Q_OBJECT
public:
StickerPanInner(QWidget *parent = 0);
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void leaveEvent(QEvent *e);
void leaveToChildEvent(QEvent *e);
void enterFromChildEvent(QEvent *e);
bool animStep(float64 ms);
void showStickerSet(uint64 setId);
void clearSelection(bool fast = false);
void refreshStickers();
void refreshRecent(bool resize = true);
void setScrollTop(int top);
void preloadImages();
public slots:
void updateSelected();
signals:
void selected(DocumentData *sticker);
void removing(uint64 setId);
void switchToEmoji();
void scrollToY(int y);
void disableScroll(bool dis);
private:
void appendSet(StickerSets::const_iterator it);
int32 countHeight();
void selectEmoji(EmojiPtr emoji);
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _animations;
int32 _top;
QList<QString> _titles;
QList<uint64> _setIds;
QList<StickerPack> _sets;
QList<QVector<float64> > _hovers;
int32 _selected, _pressedSel;
QPoint _lastMousePos;
float64 _switcherHover;
int32 _emojiWidth;
};
class EmojiPan : public TWidget, public Animated {
@ -291,11 +357,12 @@ public:
bool animStep(float64 ms);
bool eventFilter(QObject *obj, QEvent *e);
void refreshStickers();
void stickersInstalled(uint64 setId);
public slots:
void refreshStickers();
void hideStart();
void hideFinish();
@ -304,6 +371,11 @@ public slots:
void onTabChange();
void onScroll();
void onSwitch();
void onRemoveSet(uint64 setId);
void onRemoveSetSure();
void onDelayedHide();
signals:
@ -313,6 +385,8 @@ signals:
private:
void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab);
void showAll();
void hideAll();
@ -328,11 +402,20 @@ private:
BoxShadow _shadow;
FlatRadiobutton _recent, _people, _nature, _food, _celebration, _activity, _travel, _objects, _stickers;
FlatRadiobutton _recent, _people, _nature, _food, _celebration, _activity, _travel, _objects;
int32 _emojiPack;
ScrollArea _scroll;
EmojiPanInner _inner;
bool _stickersShown;
QPixmap _fromCache, _toCache;
anim::ivalue a_fromCoord, a_toCoord;
anim::fvalue a_fromAlpha, a_toAlpha;
uint64 _moveStart;
ScrollArea e_scroll;
EmojiPanInner e_inner;
ScrollArea s_scroll;
StickerPanInner s_inner;
uint64 _removingSetId;
};

View File

@ -38,7 +38,7 @@ void FileUploader::uploadMedia(MsgId msgId, const ReadyLocalMedia &media) {
}
document->status = FileUploading;
if (!media.file.isEmpty()) {
document->location = FileLocation(mtpc_storage_filePartial, media.file);
document->location = FileLocation(StorageFilePartial, media.file);
}
}
queue.insert(msgId, File(media));

View File

@ -19,6 +19,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "gui/images.h"
#include "mainwidget.h"
#include "localstorage.h"
namespace {
typedef QMap<QString, LocalImage*> LocalImages;
@ -43,7 +44,7 @@ ImagePtr::ImagePtr() : Parent(blank()) {
}
ImagePtr::ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def) :
Parent((location.type() == mtpc_fileLocation) ? (Image*)(getImage(width, height, location.c_fileLocation().vdc_id.v, location.c_fileLocation().vvolume_id.v, location.c_fileLocation().vlocal_id.v, location.c_fileLocation().vsecret.v)) : def.v()) {
Parent((location.type() == mtpc_fileLocation) ? (Image*)(getImage(StorageImageLocation(width, height, location.c_fileLocation()))) : def.v()) {
}
const QPixmap &Image::pix(int32 w, int32 h) const {
@ -517,11 +518,14 @@ int64 imageCacheSize() {
return globalAquiredSize;
}
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : w(width), h(height), loader(new mtpFileLoader(dc, volume, local, secret, size)) {
StorageImage::StorageImage(const StorageImageLocation &location, int32 size) : w(location.width), h(location.height), loader(new mtpFileLoader(location.dc, location.volume, location.local, location.secret, size)) {
}
StorageImage::StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes) : w(width), h(height), loader(0) {
StorageImage::StorageImage(const StorageImageLocation &location, QByteArray &bytes) : w(location.width), h(location.height), loader(0) {
setData(bytes);
if (location.dc) {
Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
}
}
const QPixmap &StorageImage::pixData() const {
@ -608,24 +612,27 @@ bool StorageImage::loaded() const {
return check();
}
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) {
StorageKey key(storageKey(dc, volume, local));
StorageImage *getImage(const StorageImageLocation &location, int32 size) {
StorageKey key(storageKey(location.dc, location.volume, location.local));
StorageImages::const_iterator i = storageImages.constFind(key);
if (i == storageImages.cend()) {
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, size));
i = storageImages.insert(key, new StorageImage(location, size));
}
return i.value();
}
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) {
StorageKey key(storageKey(dc, volume, local));
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes) {
StorageKey key(storageKey(location.dc, location.volume, location.local));
StorageImages::const_iterator i = storageImages.constFind(key);
if (i == storageImages.cend()) {
QByteArray bytesArr(bytes);
i = storageImages.insert(key, new StorageImage(width, height, dc, volume, local, secret, bytesArr));
i = storageImages.insert(key, new StorageImage(location, bytesArr));
} else if (!i.value()->loaded()) {
QByteArray bytesArr(bytes);
i.value()->setData(bytesArr);
if (location.dc) {
Local::writeImage(storageKey(location.dc, location.volume, location.local), StorageImageSaved(mtpToStorageType(mtpc_storage_filePartial), bytes));
}
}
return i.value();
}

View File

@ -21,6 +21,20 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
QImage imageBlur(QImage img);
struct StorageImageLocation {
StorageImageLocation() : width(0), height(0), dc(0), volume(0), local(0), secret(0) {
}
StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : width(width), height(height), dc(dc), volume(volume), local(local), secret(secret) {
}
StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : width(width), height(height), dc(location.vdc_id.v), volume(location.vvolume_id.v), local(location.vlocal_id.v), secret(location.vsecret.v) {
}
int32 width, height;
int32 dc;
uint64 volume;
int32 local;
uint64 secret;
};
class Image {
public:
@ -123,25 +137,66 @@ typedef QPair<uint64, uint64> StorageKey;
inline uint64 storageMix32To64(int32 a, int32 b) {
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&b));
}
inline StorageKey storageKey(int32 dc, const int64 &volume, int32 local) {
inline StorageKey storageKey(int32 dc, const uint64 &volume, int32 local) {
return StorageKey(storageMix32To64(dc, local), volume);
}
inline StorageKey storageKey(const MTPDfileLocation &location) {
return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v);
}
enum StorageFileType {
StorageFileUnknown = 0xaa963b05, // mtpc_storage_fileUnknown
StorageFileJpeg = 0x7efe0e, // mtpc_storage_fileJpeg
StorageFileGif = 0xcae1aadf, // mtpc_storage_fileGif
StorageFilePng = 0xa4f63c0, // mtpc_storage_filePng
StorageFilePdf = 0xae1e508d, // mtpc_storage_filePdf
StorageFileMp3 = 0x528a0677, // mtpc_storage_fileMp3
StorageFileMov = 0x4b09ebbc, // mtpc_storage_fileMov
StorageFilePartial = 0x40bc6f52, // mtpc_storage_filePartial
StorageFileMp4 = 0xb3cea0e4, // mtpc_storage_fileMp4
StorageFileWebp = 0x1081464c, // mtpc_storage_fileWebp
};
inline StorageFileType mtpToStorageType(mtpTypeId type) {
switch (type) {
case mtpc_storage_fileJpeg: return StorageFileJpeg;
case mtpc_storage_fileGif: return StorageFileGif;
case mtpc_storage_filePng: return StorageFilePng;
case mtpc_storage_filePdf: return StorageFilePdf;
case mtpc_storage_fileMp3: return StorageFileMp3;
case mtpc_storage_fileMov: return StorageFileMov;
case mtpc_storage_filePartial: return StorageFilePartial;
case mtpc_storage_fileMp4: return StorageFileMp4;
case mtpc_storage_fileWebp: return StorageFileWebp;
case mtpc_storage_fileUnknown:
default: return StorageFileUnknown;
}
}
inline mtpTypeId mtpFromStorageType(StorageFileType type) {
switch (type) {
case StorageFileGif: return mtpc_storage_fileGif;
case StorageFilePng: return mtpc_storage_filePng;
case StorageFilePdf: return mtpc_storage_filePdf;
case StorageFileMp3: return mtpc_storage_fileMp3;
case StorageFileMov: return mtpc_storage_fileMov;
case StorageFilePartial: return mtpc_storage_filePartial;
case StorageFileMp4: return mtpc_storage_fileMp4;
case StorageFileWebp: return mtpc_storage_fileWebp;
case StorageFileUnknown:
default: return mtpc_storage_fileUnknown;
}
}
struct StorageImageSaved {
StorageImageSaved() : type(mtpc_storage_fileUnknown) {
StorageImageSaved() : type(StorageFileUnknown) {
}
StorageImageSaved(mtpTypeId type, const QByteArray &data) : type(type), data(data) {
StorageImageSaved(StorageFileType type, const QByteArray &data) : type(type), data(data) {
}
mtpTypeId type;
StorageFileType type;
QByteArray data;
};
class StorageImage : public Image {
public:
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
StorageImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, QByteArray &bytes);
StorageImage(const StorageImageLocation &location, int32 size = 0);
StorageImage(const StorageImageLocation &location, QByteArray &bytes);
int32 width() const;
int32 height() const;
@ -188,8 +243,8 @@ private:
mutable mtpFileLoader *loader;
};
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
StorageImage *getImage(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes);
StorageImage *getImage(const StorageImageLocation &location, int32 size = 0);
StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes);
Image *getImage(int32 width, int32 height, const MTPFileLocation &location);
class ImagePtr : public ManagedPtr<Image> {
@ -201,9 +256,9 @@ public:
}
ImagePtr(const QPixmap &pixmap, QByteArray format) : Parent(getImage(pixmap, format)) {
}
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0) : Parent(getImage(width, height, dc, volume, local, secret, size)) {
ImagePtr(const StorageImageLocation &location, int32 size = 0) : Parent(getImage(location, size)) {
}
ImagePtr(int32 width, int32 height, int32 dc, const int64 &volume, int32 local, const int64 &secret, const QByteArray &bytes) : Parent(getImage(width, height, dc, volume, local, secret, bytes)) {
ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(getImage(location, bytes)) {
}
ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr());
};
@ -213,16 +268,16 @@ void clearAllImages();
int64 imageCacheSize();
struct FileLocation {
FileLocation(mtpTypeId type, const QString &name, const QDateTime &modified, qint32 size) : type(type), name(name), modified(modified), size(size) {
FileLocation(StorageFileType type, const QString &name, const QDateTime &modified, qint32 size) : type(type), name(name), modified(modified), size(size) {
}
FileLocation(mtpTypeId type, const QString &name) : type(type), name(name) {
FileLocation(StorageFileType type, const QString &name) : type(type), name(name) {
QFileInfo f(name);
if (f.exists()) {
qint64 s = f.size();
if (s > INT_MAX) {
this->name = QString();
size = 0;
type = mtpc_storage_fileUnknown;
type = StorageFileUnknown;
} else {
modified = f.lastModified();
size = qint32(s);
@ -230,7 +285,7 @@ struct FileLocation {
} else {
this->name = QString();
size = 0;
type = mtpc_storage_fileUnknown;
type = StorageFileUnknown;
}
}
FileLocation() : size(0) {
@ -245,7 +300,7 @@ struct FileLocation {
return (f.lastModified() == modified) && (qint32(s) == size);
}
mtpTypeId type;
StorageFileType type;
QString name;
QDateTime modified;
qint32 size;
@ -257,11 +312,34 @@ inline bool operator!=(const FileLocation &a, const FileLocation &b) {
return !(a == b);
}
enum LocationType {
UnknownFileLocation = 0,
DocumentFileLocation = 0x4e45abe9, // mtpc_inputDocumentFileLocation
AudioFileLocation = 0x74dc404d, // mtpc_inputAudioFileLocation
VideoFileLocation = 0x3d0364ec, // mtpc_inputVideoFileLocation
};
inline LocationType mtpToLocationType(mtpTypeId type) {
switch (type) {
case mtpc_inputDocumentFileLocation: return DocumentFileLocation;
case mtpc_inputAudioFileLocation: return AudioFileLocation;
case mtpc_inputVideoFileLocation: return VideoFileLocation;
default: return UnknownFileLocation;
}
}
inline mtpTypeId mtpFromLocationType(LocationType type) {
switch (type) {
case DocumentFileLocation: return mtpc_inputDocumentFileLocation;
case AudioFileLocation: return mtpc_inputAudioFileLocation;
case VideoFileLocation: return mtpc_inputVideoFileLocation;
case UnknownFileLocation:
default: return 0;
}
}
typedef QPair<uint64, uint64> MediaKey;
inline uint64 mediaMix32To64(mtpTypeId a, int32 b) {
inline uint64 mediaMix32To64(int32 a, int32 b) {
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&b));
}
inline MediaKey mediaKey(mtpTypeId type, int32 dc, const int64 &id) {
inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id) {
return MediaKey(mediaMix32To64(type, dc), id);
}
inline StorageKey mediaKey(const MTPDfileLocation &location) {

View File

@ -54,7 +54,7 @@ ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::flatScroll *st)
}
void ScrollBar::recountSize() {
setGeometry(_vertical ? QRect(rtl() ? 0 : (_area->width() - _st->width), 0, _st->width, _area->height()) : QRect(0, _area->height() - _st->width, _area->width(), _st->width));
setGeometry(_vertical ? QRect(rtl() ? 0 : (_area->width() - _st->width), _st->deltat, _st->width, _area->height() - _st->deltat - _st->deltab) : QRect(_st->deltat, _area->height() - _st->width, _area->width() - _st->deltat - _st->deltab, _st->width));
}
void ScrollBar::updateBar(bool force) {
@ -65,7 +65,7 @@ void ScrollBar::updateBar(bool force) {
_area->rangeChanged(oldMax, newMax, _vertical);
}
if (_vertical) {
int sh = _area->scrollHeight(), rh = height() - 2 * _st->deltay, h = sh ? int32((rh * int64(_area->height())) / sh) : 0;
int sh = _area->scrollHeight(), rh = height(), h = sh ? int32((rh * int64(_area->height())) / sh) : 0;
if (h >= rh || !_area->scrollTopMax() || rh < _st->minHeight) {
if (!isHidden()) hide();
bool newTopSh = (_st->topsh < 0), newBottomSh = (_st->bottomsh < 0);
@ -78,9 +78,9 @@ void ScrollBar::updateBar(bool force) {
int stm = _area->scrollTopMax(), y = stm ? int32(((rh - h) * int64(_area->scrollTop())) / stm) : 0;
if (y > rh - h) y = rh - h;
newBar = QRect(_st->deltax, y + _st->deltay, width() - 2 * _st->deltax, h);
newBar = QRect(_st->deltax, y, width() - 2 * _st->deltax, h);
} else {
int sw = _area->scrollWidth(), rw = width() - 2 * _st->deltay, w = sw ? int32((rw * int64(_area->width())) / sw) : 0;
int sw = _area->scrollWidth(), rw = width(), w = sw ? int32((rw * int64(_area->width())) / sw) : 0;
if (w >= rw || !_area->scrollLeftMax() || rw < _st->minHeight) {
if (!isHidden()) hide();
return;
@ -90,11 +90,11 @@ void ScrollBar::updateBar(bool force) {
int slm = _area->scrollLeftMax(), x = slm ? int32(((rw - w) * int64(_area->scrollLeft())) / slm) : 0;
if (x > rw - w) x = rw - w;
newBar = QRect(x + _st->deltay, _st->deltax, w, height() - 2 * _st->deltax);
newBar = QRect(x, _st->deltax, w, height() - 2 * _st->deltax);
}
if (newBar != _bar) {
_bar = newBar;
parentWidget()->update(geometry());
update();// parentWidget()->update(geometry());
}
if (_vertical) {
bool newTopSh = (_st->topsh < 0) || (_area->scrollTop() > _st->topsh), newBottomSh = (_st->bottomsh < 0) || (_area->scrollTop() < _area->scrollTopMax() - _st->bottomsh);
@ -119,15 +119,16 @@ void ScrollBar::paintEvent(QPaintEvent *e) {
if (!a_bg.current().alpha() && !a_bar.current().alpha()) return;
QPainter p(this);
int32 deltax = _vertical ? _st->deltax : _st->deltay, deltay = _vertical ? _st->deltay : _st->deltax;
int32 deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0;
int32 deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax;
p.setPen(Qt::NoPen);
if (_st->round) {
p.setBrush(a_bg.current());
p.drawRoundedRect(QRect(deltax, deltay, width() - 2 * deltax, height() - 2 * deltay), _st->round, _st->round);
p.drawRoundedRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), _st->round, _st->round);
p.setBrush(a_bar.current());
p.drawRoundedRect(_bar, _st->round, _st->round);
} else {
p.fillRect(QRect(deltax, deltay, width() - 2 * deltax, height() - 2 * deltay), a_bg.current());
p.fillRect(QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab), a_bg.current());
p.fillRect(_bar, a_bar.current());
}
}
@ -143,7 +144,7 @@ bool ScrollBar::animStep(float64 ms) {
a_bg.update(dt, anim::linear);
a_bar.update(dt, anim::linear);
}
parentWidget()->update(geometry());
update();// parentWidget()->update(geometry());
return res;
}
@ -176,6 +177,8 @@ void ScrollBar::leaveEvent(QEvent *e) {
anim::start(this);
if (_hideIn >= 0) {
_hideTimer.start(_hideIn);
} else if (_st->hiding) {
hideTimeout(_st->hiding);
}
}
_over = _overbar = false;
@ -193,7 +196,7 @@ void ScrollBar::mouseMoveEvent(QMouseEvent *e) {
}
if (_moving) {
int delta = 0, barDelta = _vertical ? (_area->height() - _bar.height()) : (_area->width() - _bar.width());
if (barDelta) {
if (barDelta > 0) {
QPoint d = (e->globalPos() - _dragStart);
delta = int32((_vertical ? (d.y() * int64(_area->scrollTopMax())) : (d.x() * int64(_area->scrollLeftMax()))) / barDelta);
}
@ -209,7 +212,10 @@ void ScrollBar::mousePressEvent(QMouseEvent *e) {
if (_overbar) {
_startFrom = _connected->value();
} else {
_startFrom = _vertical ? int32((e->pos().y() * int64(_area->scrollTopMax())) / height()) : ((e->pos().x() * int64(_area->scrollLeftMax())) / width());
int32 val = _vertical ? e->pos().y() : e->pos().x(), div = _vertical ? height() : width();
val = (val <= _st->deltat) ? 0 : (val - _st->deltat);
div = (div <= _st->deltat + _st->deltab) ? 1 : (div - _st->deltat - _st->deltab);
_startFrom = _vertical ? int32((val * int64(_area->scrollTopMax())) / div) : ((val * int64(_area->scrollLeftMax())) / div);
_connected->setValue(_startFrom);
if (!_overbar) {
_overbar = true;

View File

@ -736,10 +736,13 @@ void TextLink::onClick(Qt::MouseButton button) const {
QString url = TextLink::encoded();
QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
QRegularExpressionMatch telegramMeStickers = QRegularExpression(qsl("^https?://telegram\\.me/addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url);
if (telegramMeUser.hasMatch()) {
App::openUserByName(telegramMeUser.captured(1));
} else if (telegramMeGroup.hasMatch()) {
App::joinGroupByHash(telegramMeGroup.captured(1));
} else if (telegramMeStickers.hasMatch()) {
App::stickersBox(telegramMeStickers.captured(1));
} else if (QRegularExpression(qsl("^tg://[a-zA-Z0-9]+"), QRegularExpression::CaseInsensitiveOption).match(url).hasMatch()) {
App::openLocalUrl(url);
} else {

View File

@ -701,6 +701,13 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_menu->addAction(lang(lng_context_reply_msg), historyWidget, SLOT(onReplyToMessage()));
}
if (item && !isUponSelected && !_contextMenuLnk) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(msg->getMedia())) {
DocumentData *doc = sticker->document();
if (doc && doc->sticker && doc->sticker->set.type() != mtpc_inputStickerSetEmpty) {
if (!_menu) _menu = new ContextMenu(this);
_menu->addAction(lang(lng_context_pack_info), historyWidget, SLOT(onStickerPackInfo()));
}
}
QString contextMenuText = item->selectedText(FullItemSel);
if (!contextMenuText.isEmpty() && (!msg || !msg->getMedia() || msg->getMedia()->type() != MediaTypeSticker)) {
if (!_menu) _menu = new ContextMenu(this);
@ -771,7 +778,9 @@ void HistoryList::onMenuDestroy(QObject *obj) {
}
void HistoryList::copySelectedText() {
QApplication::clipboard()->setText(getSelectedText());
QString sel = getSelectedText();
DEBUG_LOG(("Setting selected text to clipboard: %1").arg(sel));
QApplication::clipboard()->setText(sel);
}
void HistoryList::openContextUrl() {
@ -784,6 +793,7 @@ void HistoryList::openContextUrl() {
void HistoryList::copyContextUrl() {
QString enc = _contextMenuLnk->encoded();
if (!enc.isEmpty()) {
DEBUG_LOG(("Setting text to clipboard from context url: %1").arg(enc));
QApplication::clipboard()->setText(enc);
}
}
@ -857,6 +867,7 @@ void HistoryList::copyContextText() {
QString contextMenuText = item->selectedText(FullItemSel);
if (!contextMenuText.isEmpty()) {
DEBUG_LOG(("Setting text to clipboard from context menu: %1").arg(contextMenuText));
QApplication::clipboard()->setText(contextMenuText);
}
}
@ -1569,7 +1580,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
, _previewCancelled(false)
, _replyForwardPressed(false)
, _replyReturn(0)
, _lastStickersUpdate(0)
, _stickersUpdateRequest(0)
, _loadingMessages(false)
, histRequestsCount(0)
@ -1687,6 +1697,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
}
void HistoryWidget::start() {
connect(App::main(), SIGNAL(stickersUpdated()), &_emojiPan, SLOT(refreshStickers()));
updateRecentStickers();
connect(App::api(), SIGNAL(fullPeerLoaded(PeerData*)), this, SLOT(onPeerLoaded(PeerData*)));
}
@ -1766,6 +1777,10 @@ void HistoryWidget::updateRecentStickers() {
_emojiPan.refreshStickers();
}
void HistoryWidget::stickersInstalled(uint64 setId) {
_emojiPan.stickersInstalled(setId);
}
void HistoryWidget::typingDone(const MTPBool &result, mtpRequestId req) {
if (_typingRequest == req) {
_typingRequest = 0;
@ -1795,116 +1810,210 @@ void HistoryWidget::activate() {
}
void HistoryWidget::updateStickers() {
if (_lastStickersUpdate && getms(true) < _lastStickersUpdate + StickersUpdateTimeout) return;
if (cLastStickersUpdate() && getms(true) < cLastStickersUpdate() + StickersUpdateTimeout) return;
if (_stickersUpdateRequest) return;
_stickersUpdateRequest = MTP::send(MTPmessages_GetAllStickers(MTP_string(cStickersHash())), rpcDone(&HistoryWidget::stickersGot), rpcFail(&HistoryWidget::stickersFailed));
}
void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
_lastStickersUpdate = getms(true);
cSetLastStickersUpdate(getms(true));
_stickersUpdateRequest = 0;
if (stickers.type() == mtpc_messages_allStickers) {
const MTPDmessages_allStickers &d(stickers.c_messages_allStickers());
if (stickers.type() != mtpc_messages_allStickers) return;
const MTPDmessages_allStickers &d(stickers.c_messages_allStickers());
EmojiStickersMap map;
AllStickers all;
EmojiStickersMap map;
const QVector<MTPDocument> &d_docs(d.vdocuments.c_vector().v);
const QVector<MTPStickerSet> &d_sets(d.vsets.c_vector().v);
const QVector<MTPDocument> &docs(d.vdocuments.c_vector().v);
QByteArray wasHash = cStickersHash();
cSetStickersHash(qba(d.vhash));
QSet<DocumentData*> found;
const RecentStickerPack &recent(cRecentStickers());
RecentStickerPack add;
add.reserve(docs.size());
ushort addValue = recent.isEmpty() ? 1 : qAbs(recent.front().second);
for (int32 i = 0, l = docs.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(docs.at(i));
if (!doc) continue;
int32 j = 0, s = recent.size();
for (; j < s; ++j) {
if (doc == recent.at(j).first) {
StickerSets &sets(cRefStickerSets());
StickerSets::iterator def = sets.find(DefaultStickerSetId);
if (def == sets.cend()) {
def = sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, qsl("Great Minds"), QString()));
}
for (int32 i = 0; i < d_sets.size(); ++i) {
if (d_sets.at(i).type() == mtpc_stickerSet) {
const MTPDstickerSet &set(d_sets.at(i).c_stickerSet());
StickerSets::iterator i = sets.find(set.vid.v);
if (i == sets.cend()) {
i = sets.insert(set.vid.v, StickerSet(set.vid.v, set.vaccess_hash.v, qs(set.vtitle), qs(set.vshort_name)));
} else {
i->access = set.vaccess_hash.v;
i->title = qs(set.vtitle);
i->shortName = qs(set.vshort_name);
}
}
}
StickerSets::iterator custom = sets.find(CustomStickerSetId);
bool added = false, removed = false;
QSet<DocumentData*> found;
QMap<uint64, int32> wasCount;
for (int32 i = 0, l = d_docs.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(d_docs.at(i));
if (!doc || !doc->sticker) continue;
switch (doc->sticker->set.type()) {
case mtpc_inputStickerSetEmpty: { // default set - great minds
if (!wasCount.contains(DefaultStickerSetId)) wasCount.insert(DefaultStickerSetId, def->stickers.size());
if (def->stickers.indexOf(doc) < 0) {
def->stickers.push_back(doc);
added = true;
} else {
found.insert(doc);
}
} break;
case mtpc_inputStickerSetID: {
StickerSets::iterator it = sets.find(doc->sticker->set.c_inputStickerSetID().vid.v);
if (it == sets.cend()) {
LOG(("Sticker Set not found by ID: %1").arg(doc->sticker->set.c_inputStickerSetID().vid.v));
} else {
if (!wasCount.contains(it->id)) wasCount.insert(it->id, it->stickers.size());
if (it->stickers.indexOf(doc) < 0) {
it->stickers.push_back(doc);
added = true;
} else {
found.insert(doc);
}
}
} break;
case mtpc_inputStickerSetShortName: {
QString name = qs(doc->sticker->set.c_inputStickerSetShortName().vshort_name).toLower().trimmed();
StickerSets::iterator it = sets.begin();
for (; it != sets.cend(); ++it) {
if (it->shortName.toLower().trimmed() == name) {
break;
}
}
if (j < s) continue;
add.push_back(qMakePair(doc, addValue));
if (it == sets.cend()) {
LOG(("Sticker Set not found by name: %1").arg(name));
} else {
if (!wasCount.contains(it->id)) wasCount.insert(it->id, it->stickers.size());
if (it->stickers.indexOf(doc) < 0) {
it->stickers.push_back(doc);
added = true;
} else {
found.insert(doc);
}
}
} break;
}
bool needRemove = false;
for (int32 i = 0, l = recent.size(); i < l; ++i) {
if (recent.at(i).second > 0 && !found.contains(recent.at(i).first)) {
needRemove = true;
break;
if (custom != sets.cend()) {
int32 index = custom->stickers.indexOf(doc);
if (index >= 0) {
custom->stickers.removeAt(index);
removed = true;
}
}
if (!add.isEmpty() || needRemove) {
if (needRemove) {
for (int32 i = 0, l = recent.size(); i < l; ++i) {
if (recent.at(i).second <= 0 || found.contains(recent.at(i).first)) {
add.push_back(recent.at(i));
}
if (custom != sets.cend() && custom->stickers.isEmpty()) {
sets.erase(custom);
custom = sets.end();
}
bool writeRecent = false;
RecentStickerPack &recent(cGetRecentStickers());
for (StickerSets::iterator it = sets.begin(); it != sets.cend();) {
if (it->id == CustomStickerSetId || it->id == RecentStickerSetId) {
++it;
continue;
}
QMap<uint64, int32>::const_iterator was = wasCount.constFind(it->id);
if (was == wasCount.cend()) { // no such stickers added
for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) {
if (it->stickers.indexOf(i->first) >= 0) {
i = recent.erase(i);
writeRecent = true;
} else {
++i;
}
}
it = sets.erase(it);
removed = true;
} else {
for (int32 j = 0, l = was.value(); j < l;) {
if (found.contains(it->stickers.at(j))) {
++j;
} else {
for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) {
if (it->stickers.at(j) == i->first) {
i = recent.erase(i);
writeRecent = true;
} else {
++i;
}
}
it->stickers.removeAt(j);
--l;
removed = true;
}
}
if (it->stickers.isEmpty()) {
it = sets.erase(it);
} else {
++it;
}
}
}
if (added || removed || cStickersHash() != wasHash) {
Local::writeStickers();
}
if (writeRecent) {
Local::writeUserSettings();
}
const QVector<MTPStickerPack> &packs(d.vpacks.c_vector().v);
for (int32 i = 0, l = packs.size(); i < l; ++i) {
if (packs.at(i).type() == mtpc_stickerPack) {
const MTPDstickerPack &p(packs.at(i).c_stickerPack());
QString emoticon(qs(p.vemoticon));
EmojiPtr e = 0;
for (const QChar *ch = emoticon.constData(), *end = emoticon.constEnd(); ch != end; ++ch) {
int len = 0;
e = emojiFromText(ch, end, len);
if (e) break;
if (ch + 1 < end && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch;
}
if (e) {
const QVector<MTPlong> docs(p.vdocuments.c_vector().v);
if (!docs.isEmpty()) {
for (int32 j = 0, s = docs.size(); j < s; ++j) {
DocumentData *doc = App::document(docs.at(j).v);
map.insert(doc, e);
}
}
} else {
add += recent;
}
cSetRecentStickers(add);
Local::writeRecentStickers();
}
const QVector<MTPStickerPack> &packs(d.vpacks.c_vector().v);
for (int32 i = 0, l = packs.size(); i < l; ++i) {
if (packs.at(i).type() == mtpc_stickerPack) {
const MTPDstickerPack &p(packs.at(i).c_stickerPack());
QString emoticon(qs(p.vemoticon));
EmojiPtr e = 0;
for (const QChar *ch = emoticon.constData(), *end = emoticon.constEnd(); ch != end; ++ch) {
int len = 0;
e = emojiFromText(ch, end, len);
if (e) break;
if (ch + 1 < end && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch;
}
if (e) {
const QVector<MTPlong> docs(p.vdocuments.c_vector().v);
if (!docs.isEmpty()) {
StickerPack &pack(all[e]);
pack.reserve(pack.size() + docs.size());
for (int32 j = 0, s = docs.size(); j < s; ++j) {
DocumentData *doc = App::document(docs.at(j).v);
pack.push_back(doc);
map.insert(doc, e);
}
}
} else {
LOG(("Sticker Error: Could not find emoji for string: %1").arg(emoticon));
}
LOG(("Sticker Error: Could not find emoji for string: %1").arg(emoticon));
}
}
cSetStickers(all);
cSetStickersHash(qba(d.vhash));
cSetEmojiStickers(map);
const DocumentItems &items(App::documentItems());
for (EmojiStickersMap::const_iterator i = map.cbegin(), e = map.cend(); i != e; ++i) {
DocumentItems::const_iterator j = items.constFind(i.key());
if (j != items.cend()) {
for (HistoryItemsMap::const_iterator k = j->cbegin(), end = j->cend(); k != end; ++k) {
k.key()->updateStickerEmoji();
}
}
}
// updateStickerPan();
_emojiPan.refreshStickers();
}
cSetEmojiStickers(map);
const DocumentItems &items(App::documentItems());
for (EmojiStickersMap::const_iterator i = map.cbegin(), e = map.cend(); i != e; ++i) {
DocumentItems::const_iterator j = items.constFind(i.key());
if (j != items.cend()) {
for (HistoryItemsMap::const_iterator k = j->cbegin(), end = j->cend(); k != end; ++k) {
k.key()->updateStickerEmoji();
}
}
}
// updateStickerPan();
if (App::main()) emit App::main()->stickersUpdated();
}
bool HistoryWidget::stickersFailed(const RPCError &error) {
if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
_lastStickersUpdate = getms(true);
cSetLastStickersUpdate(getms(true));
_stickersUpdateRequest = 0;
return true;
}
@ -3683,6 +3792,16 @@ void HistoryWidget::onReplyForwardPreviewCancel() {
}
}
void HistoryWidget::onStickerPackInfo() {
if (HistoryMessage *item = dynamic_cast<HistoryMessage*>(App::contextItem())) {
if (HistorySticker *sticker = dynamic_cast<HistorySticker*>(item->getMedia())) {
if (sticker->document() && sticker->document()->sticker && sticker->document()->sticker->set.type() != mtpc_inputStickerSetEmpty) {
App::main()->stickersBox(sticker->document()->sticker->set);
}
}
}
}
void HistoryWidget::previewCancel() {
MTP::cancel(_previewRequest);
_previewRequest = 0;

View File

@ -298,6 +298,7 @@ public:
void updateTyping(bool typing = true);
// void updateStickerPan();
void updateRecentStickers();
void stickersInstalled(uint64 setId);
void typingDone(const MTPBool &result, mtpRequestId req);
void destroyData();
@ -379,6 +380,8 @@ public slots:
void onReplyToMessage();
void onReplyForwardPreviewCancel();
void onStickerPackInfo();
void onPreviewParse();
void onPreviewCheck();
void onPreviewTimeout();
@ -473,7 +476,6 @@ private:
void stickersGot(const MTPmessages_AllStickers &stickers);
bool stickersFailed(const RPCError &error);
uint64 _lastStickersUpdate;
mtpRequestId _stickersUpdateRequest;
void writeDraft(MsgId *replyTo = 0, const QString *text = 0, const MessageCursor *cursor = 0, bool *previewCancelled = 0);

View File

@ -21,6 +21,11 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "lang.h"
namespace {
enum StickerSetType {
StickerSetTypeEmpty = 0,
StickerSetTypeID = 1,
StickerSetTypeShortName = 2,
};
typedef quint64 FileKey;
@ -140,6 +145,10 @@ namespace {
return sizeof(quint32) + str.size() * sizeof(ushort);
}
uint32 _bytearraySize(const QByteArray &arr) {
return sizeof(quint32) + arr.size();
}
QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted;
mtpAuthKey _oldKey, _settingsKey, _passKey, _localKey;
@ -484,17 +493,18 @@ namespace {
FileKey _dataNameKey = 0;
enum { // Local Storage Keys
lskUserMap = 0,
lskDraft, // data: PeerId peer
lskDraftPosition, // data: PeerId peer
lskImages, // data: StorageKey location
lskLocations, // no data
lskStickers, // data: StorageKey location
lskAudios, // data: StorageKey location
lskRecentStickers, // no data
lskBackground, // no data
lskUserSettings, // no data
lskRecentHashtags, // no data
lskUserMap = 0x00,
lskDraft = 0x01, // data: PeerId peer
lskDraftPosition = 0x02, // data: PeerId peer
lskImages = 0x03, // data: StorageKey location
lskLocations = 0x04, // no data
lskStickerImages = 0x05, // data: StorageKey location
lskAudios = 0x06, // data: StorageKey location
lskRecentStickersOld = 0x07, // no data
lskBackground = 0x08, // no data
lskUserSettings = 0x09, // no data
lskRecentHashtags = 0x0a, // no data
lskStickers = 0x0b, // no data
};
typedef QMap<PeerId, FileKey> DraftsMap;
@ -509,7 +519,7 @@ namespace {
FileLocationPairs _fileLocationPairs;
FileKey _locationsKey = 0;
FileKey _recentStickersKey = 0;
FileKey _recentStickersKeyOld = 0, _stickersKey = 0;
FileKey _backgroundKey = 0;
bool _backgroundWasRead = false;
@ -520,7 +530,7 @@ namespace {
typedef QPair<FileKey, qint32> FileDesc; // file, size
typedef QMap<StorageKey, FileDesc> StorageMap;
StorageMap _imagesMap, _stickersMap, _audiosMap;
StorageMap _imagesMap, _stickerImagesMap, _audiosMap;
int32 _storageImagesSize = 0, _storageStickersSize = 0, _storageAudiosSize = 0;
bool _mapChanged = false;
@ -585,7 +595,7 @@ namespace {
locations.stream >> first >> second >> type >> loc.name >> loc.modified >> loc.size;
MediaKey key(first, second);
loc.type = type;
loc.type = StorageFileType(type);
if (loc.check()) {
_fileLocations.insert(key, loc);
@ -953,6 +963,14 @@ namespace {
cSetRecentEmojisPreload(v);
} break;
case dbiRecentStickers: {
RecentStickerPreload v;
stream >> v;
if (!_checkStreamStatus(stream)) return false;
cSetRecentStickersPreload(v);
} break;
case dbiEmojiVariants: {
EmojiColorVariants v;
stream >> v;
@ -1192,8 +1210,9 @@ namespace {
uint32 size = 11 * (sizeof(quint32) + sizeof(qint32));
size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath());
size += sizeof(quint32) + sizeof(qint32) + cGetRecentEmojis().size() * (sizeof(uint64) + sizeof(ushort));
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));
size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort));
size += sizeof(quint32) + _stringSize(cDialogLastPath());
EncryptedDescriptor data(size);
@ -1211,14 +1230,27 @@ namespace {
data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab());
data.stream << quint32(dbiDialogLastPath) << cDialogLastPath();
RecentEmojisPreload v;
v.reserve(cGetRecentEmojis().size());
for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) {
v.push_back(qMakePair(emojiKey(i->first), i->second));
{
RecentEmojisPreload v(cRecentEmojisPreload());
if (v.isEmpty()) {
v.reserve(cGetRecentEmojis().size());
for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) {
v.push_back(qMakePair(emojiKey(i->first), i->second));
}
}
data.stream << quint32(dbiRecentEmojis) << v;
}
data.stream << quint32(dbiRecentEmojis) << v;
data.stream << quint32(dbiEmojiVariants) << cEmojiVariants();
{
RecentStickerPreload v(cRecentStickersPreload());
if (v.isEmpty()) {
v.reserve(cGetRecentStickers().size());
for (RecentStickerPack::const_iterator i = cGetRecentStickers().cbegin(), e = cGetRecentStickers().cend(); i != e; ++i) {
v.push_back(qMakePair(i->first->id, i->second));
}
}
data.stream << quint32(dbiRecentStickers) << v;
}
FileWriteDescriptor file(_userSettingsKey);
file.writeEncrypted(data);
@ -1340,9 +1372,9 @@ namespace {
DraftsMap draftsMap, draftsPositionsMap;
DraftsNotReadMap draftsNotReadMap;
StorageMap imagesMap, stickersMap, audiosMap;
StorageMap imagesMap, stickerImagesMap, audiosMap;
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
quint64 locationsKey = 0, recentStickersKeyOld = 0, stickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
while (!map.stream.atEnd()) {
quint32 keyType;
map.stream >> keyType;
@ -1380,7 +1412,7 @@ namespace {
storageImagesSize += size;
}
} break;
case lskStickers: {
case lskStickerImages: {
quint32 count = 0;
map.stream >> count;
for (quint32 i = 0; i < count; ++i) {
@ -1388,7 +1420,7 @@ namespace {
quint64 first, second;
qint32 size;
map.stream >> key >> first >> second >> size;
stickersMap.insert(StorageKey(first, second), FileDesc(key, size));
stickerImagesMap.insert(StorageKey(first, second), FileDesc(key, size));
storageStickersSize += size;
}
} break;
@ -1407,8 +1439,8 @@ namespace {
case lskLocations: {
map.stream >> locationsKey;
} break;
case lskRecentStickers: {
map.stream >> recentStickersKey;
case lskRecentStickersOld: {
map.stream >> recentStickersKeyOld;
} break;
case lskBackground: {
map.stream >> backgroundKey;
@ -1419,6 +1451,9 @@ namespace {
case lskRecentHashtags: {
map.stream >> recentHashtagsKey;
} break;
case lskStickers: {
map.stream >> stickersKey;
} break;
default:
LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
return Local::ReadMapFailed;
@ -1434,13 +1469,14 @@ namespace {
_imagesMap = imagesMap;
_storageImagesSize = storageImagesSize;
_stickersMap = stickersMap;
_stickerImagesMap = stickerImagesMap;
_storageStickersSize = storageStickersSize;
_audiosMap = audiosMap;
_storageAudiosSize = storageAudiosSize;
_locationsKey = locationsKey;
_recentStickersKey = recentStickersKey;
_recentStickersKeyOld = recentStickersKeyOld;
_stickersKey = stickersKey;
_backgroundKey = backgroundKey;
_userSettingsKey = userSettingsKey;
_recentHashtagsKey = recentHashtagsKey;
@ -1500,10 +1536,11 @@ namespace {
if (!_draftsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
if (!_draftsPositionsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsPositionsMap.size() * sizeof(quint64) * 2;
if (!_imagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _imagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
if (!_stickerImagesMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickerImagesMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64);
if (_stickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
if (_recentHashtagsKey) mapSize += sizeof(quint32) + sizeof(quint64);
@ -1526,9 +1563,9 @@ namespace {
mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
}
}
if (!_stickersMap.isEmpty()) {
mapData.stream << quint32(lskStickers) << quint32(_stickersMap.size());
for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
if (!_stickerImagesMap.isEmpty()) {
mapData.stream << quint32(lskStickerImages) << quint32(_stickerImagesMap.size());
for (StorageMap::const_iterator i = _stickerImagesMap.cbegin(), e = _stickerImagesMap.cend(); i != e; ++i) {
mapData.stream << quint64(i.value().first) << quint64(i.key().first) << quint64(i.key().second) << qint32(i.value().second);
}
}
@ -1541,8 +1578,11 @@ namespace {
if (_locationsKey) {
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
}
if (_recentStickersKey) {
mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey);
if (_recentStickersKeyOld) {
mapData.stream << quint32(lskRecentStickersOld) << quint64(_recentStickersKeyOld);
}
if (_stickersKey) {
mapData.stream << quint32(lskStickers) << quint64(_stickersKey);
}
if (_backgroundKey) {
mapData.stream << quint32(lskBackground) << quint64(_backgroundKey);
@ -1786,9 +1826,9 @@ namespace Local {
_draftsPositionsMap.clear();
_imagesMap.clear();
_draftsNotReadMap.clear();
_stickersMap.clear();
_stickerImagesMap.clear();
_audiosMap.clear();
_locationsKey = _recentStickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
_locationsKey = _recentStickersKeyOld = _stickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
_mapChanged = true;
_writeMap(WriteMapNow);
@ -1996,13 +2036,13 @@ namespace Local {
if (_imagesMap.constFind(location) != _imagesMap.cend()) return;
QByteArray fmt = image->savedFormat();
mtpTypeId format = 0;
StorageFileType format = StorageFileUnknown;
if (fmt == "JPG") {
format = mtpc_storage_fileJpeg;
format = StorageFileJpeg;
} else if (fmt == "PNG") {
format = mtpc_storage_filePng;
format = StorageFilePng;
} else if (fmt == "GIF") {
format = mtpc_storage_fileGif;
format = StorageFileGif;
}
if (format) {
image->forget();
@ -2052,7 +2092,7 @@ namespace Local {
quint32 imageType;
draft.stream >> locFirst >> locSecond >> imageType >> imageData;
return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(imageType, imageData) : StorageImageSaved();
return (locFirst == location.first && locSecond == location.second) ? StorageImageSaved(StorageFileType(imageType), imageData) : StorageImageSaved();
}
int32 hasImages() {
@ -2063,13 +2103,13 @@ namespace Local {
return _storageImagesSize;
}
void writeSticker(const StorageKey &location, const QByteArray &sticker, bool overwrite) {
void writeStickerImage(const StorageKey &location, const QByteArray &sticker, bool overwrite) {
if (!_working()) return;
qint32 size = _storageStickerSize(sticker.size());
StorageMap::const_iterator i = _stickersMap.constFind(location);
if (i == _stickersMap.cend()) {
i = _stickersMap.insert(location, FileDesc(genKey(UserPath), size));
StorageMap::const_iterator i = _stickerImagesMap.constFind(location);
if (i == _stickerImagesMap.cend()) {
i = _stickerImagesMap.insert(location, FileDesc(genKey(UserPath), size));
_storageStickersSize += size;
_mapChanged = true;
_writeMap();
@ -2083,20 +2123,20 @@ namespace Local {
if (i.value().second != size) {
_storageStickersSize += size;
_storageStickersSize -= i.value().second;
_stickersMap[location].second = size;
_stickerImagesMap[location].second = size;
}
}
QByteArray readSticker(const StorageKey &location) {
StorageMap::iterator j = _stickersMap.find(location);
if (j == _stickersMap.cend()) {
QByteArray readStickerImage(const StorageKey &location) {
StorageMap::iterator j = _stickerImagesMap.find(location);
if (j == _stickerImagesMap.cend()) {
return QByteArray();
}
FileReadDescriptor draft;
if (!readEncryptedFile(draft, j.value().first, UserPath)) {
clearKey(j.value().first, UserPath);
_storageStickersSize -= j.value().second;
_stickersMap.erase(j);
_stickerImagesMap.erase(j);
return QByteArray();
}
@ -2108,7 +2148,7 @@ namespace Local {
}
int32 hasStickers() {
return _stickersMap.size();
return _stickerImagesMap.size();
}
qint64 storageStickersSize() {
@ -2167,56 +2207,91 @@ namespace Local {
return _storageAudiosSize;
}
void writeRecentStickers() {
void writeStickers() {
if (!_working()) return;
const RecentStickerPack &recent(cRecentStickers());
if (recent.isEmpty()) {
if (_recentStickersKey) {
clearKey(_recentStickersKey);
_recentStickersKey = 0;
const StickerSets &sets(cStickerSets());
if (sets.isEmpty()) {
if (_stickersKey) {
clearKey(_stickersKey);
_stickersKey = 0;
_mapChanged = true;
}
_writeMap();
} else {
if (!_recentStickersKey) {
_recentStickersKey = genKey();
if (!_stickersKey) {
_stickersKey = genKey();
_mapChanged = true;
_writeMap(WriteMapFast);
}
quint32 size = 0;
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
DocumentData *doc = i->first;
if (doc->status == FileFailed) continue;
quint32 size = sizeof(quint32) + _bytearraySize(cStickersHash());
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
if (i->stickers.isEmpty()) continue;
// id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt
size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker ? doc->sticker->alt : QString());
// id + access + title + shortName + stickersCount
size += sizeof(quint64) * 2 + _stringSize(i->title) + _stringSize(i->shortName) + sizeof(quint32);
for (StickerPack::const_iterator j = i->stickers.cbegin(), e = i->stickers.cend(); j != e; ++j) {
DocumentData *doc = *j;
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set
size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker->alt) + sizeof(qint32);
// thumb-width + thumb-height + thumb-dc + thumb-volume + thumb-local + thumb-secret
size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64);
}
}
EncryptedDescriptor data(size);
for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) {
DocumentData *doc = i->first;
if (doc->status == FileFailed) continue;
data.stream << quint32(sets.size()) << cStickersHash();
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
if (i->stickers.isEmpty()) continue;
data.stream << quint64(doc->id) << qint16(i->second) << 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) << (doc->sticker ? doc->sticker->alt : QString());
data.stream << quint64(i->id) << quint64(i->access) << i->title << i->shortName << quint32(i->stickers.size());
for (StickerPack::const_iterator j = i->stickers.cbegin(), e = i->stickers.cend(); j != e; ++j) {
DocumentData *doc = *j;
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) << doc->sticker->alt;
switch (doc->sticker->set.type()) {
case mtpc_inputStickerSetID: {
data.stream << qint32(StickerSetTypeID);
} break;
case mtpc_inputStickerSetShortName: {
data.stream << qint32(StickerSetTypeShortName);
} break;
case mtpc_inputStickerSetEmpty:
default: {
data.stream << qint32(StickerSetTypeEmpty);
} break;
}
const StorageImageLocation &loc(doc->sticker->loc);
data.stream << qint32(loc.width) << qint32(loc.height) << qint32(loc.dc) << quint64(loc.volume) << qint32(loc.local) << quint64(loc.secret);
}
}
FileWriteDescriptor file(_recentStickersKey);
FileWriteDescriptor file(_stickersKey);
file.writeEncrypted(data);
}
}
void readRecentStickers() {
if (!_recentStickersKey) return;
void importOldRecentStickers() {
if (!_recentStickersKeyOld) return;
FileReadDescriptor stickers;
if (!readEncryptedFile(stickers, _recentStickersKey)) {
clearKey(_recentStickersKey);
_recentStickersKey = 0;
if (!readEncryptedFile(stickers, _recentStickersKeyOld)) {
clearKey(_recentStickersKeyOld);
_recentStickersKeyOld = 0;
_writeMap();
return;
}
StickerSets &sets(cRefStickerSets());
sets.clear();
RecentStickerPack &recent(cRefRecentStickers());
recent.clear();
cSetStickersHash(QByteArray());
StickerSet &def(sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, qsl("Great Minds"), QString())).value());
StickerSet &custom(sets.insert(CustomStickerSetId, StickerSet(CustomStickerSetId, 0, lang(lng_custom_stickers), QString())).value());
QMap<uint64, bool> read;
RecentStickerPack recent;
while (!stickers.stream.atEnd()) {
quint64 id, access;
QString name, mime, alt;
@ -2226,7 +2301,7 @@ namespace Local {
if (stickers.version >= 7021) {
stickers.stream >> alt;
}
if (read.contains(id)) continue;
if (!value || read.contains(id)) continue;
read.insert(id, true);
QVector<MTPDocumentAttribute> attributes;
@ -2240,10 +2315,109 @@ namespace Local {
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
}
recent.push_back(qMakePair(App::document(id, 0, access, date, attributes, mime, ImagePtr(), dc, size), value));
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, ImagePtr(), dc, size, StorageImageLocation());
if (!doc->sticker) continue;
if (value > 0) {
def.stickers.push_back(doc);
} else {
custom.stickers.push_back(doc);
}
if (recent.size() < StickerPanPerRow * StickerPanRowsPerPage && qAbs(value) > 1) recent.push_back(qMakePair(doc, qAbs(value)));
}
if (def.stickers.isEmpty()) sets.remove(DefaultStickerSetId);
if (custom.stickers.isEmpty()) sets.remove(CustomStickerSetId);
writeStickers();
writeUserSettings();
clearKey(_recentStickersKeyOld);
_recentStickersKeyOld = 0;
_writeMap();
}
void readStickers() {
if (!_stickersKey) {
return importOldRecentStickers();
}
cSetRecentStickers(recent);
FileReadDescriptor stickers;
if (!readEncryptedFile(stickers, _stickersKey)) {
clearKey(_stickersKey);
_stickersKey = 0;
_writeMap();
return;
}
StickerSets &sets(cRefStickerSets());
sets.clear();
quint32 cnt;
QByteArray hash;
stickers.stream >> cnt >> hash;
for (int32 i = 0; i < cnt; ++i) {
quint64 setId = 0, setAccess = 0;
QString setTitle, setShortName;
quint32 scnt = 0;
stickers.stream >> setId >> setAccess >> setTitle >> setShortName >> scnt;
if (setId == DefaultStickerSetId) {
setTitle = qsl("Great Minds");
} else if (setId == CustomStickerSetId) {
setTitle = lang(lng_custom_stickers);
}
StickerSet &set(sets.insert(setId, StickerSet(setId, setAccess, setTitle, setShortName)).value());
set.stickers.reserve(scnt);
QMap<uint64, bool> read;
for (int32 j = 0; j < scnt; ++j) {
quint64 id, access;
QString name, mime, alt;
qint32 date, dc, size, width, height, type, typeOfSet;
stickers.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type >> alt >> typeOfSet;
qint32 thumbWidth, thumbHeight, thumbDc, thumbLocal;
quint64 thumbVolume, thumbSecret;
stickers.stream >> thumbWidth >> thumbHeight >> thumbDc >> thumbVolume >> thumbLocal >> thumbSecret;
if (read.contains(id)) continue;
read.insert(id, true);
if (setId == DefaultStickerSetId || setId == CustomStickerSetId) {
typeOfSet = StickerSetTypeEmpty;
}
QVector<MTPDocumentAttribute> attributes;
if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name)));
if (type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (type == StickerDocument) {
switch (typeOfSet) {
case StickerSetTypeID: {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetID(MTP_long(setId), MTP_long(setAccess))));
} break;
case StickerSetTypeShortName: {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetShortName(MTP_string(setShortName))));
} break;
case StickerSetTypeEmpty:
default: {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
} break;
}
}
if (width > 0 && height > 0) {
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
}
StorageImageLocation thumb(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret);
DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.dc ? ImagePtr(thumb) : ImagePtr(), dc, size, thumb);
if (!doc->sticker) continue;
set.stickers.push_back(doc);
}
}
cSetStickersHash(hash);
}
void writeBackground(int32 id, const QImage &img) {
@ -2412,8 +2586,8 @@ namespace Local {
_storageImagesSize = 0;
_mapChanged = true;
}
if (!_stickersMap.isEmpty()) {
_stickersMap.clear();
if (!_stickerImagesMap.isEmpty()) {
_stickerImagesMap.clear();
_storageStickersSize = 0;
_mapChanged = true;
}
@ -2434,8 +2608,12 @@ namespace Local {
_locationsKey = 0;
_mapChanged = true;
}
if (_recentStickersKey) {
_recentStickersKey = 0;
if (_recentStickersKeyOld) {
_recentStickersKeyOld = 0;
_mapChanged = true;
}
if (_stickersKey) {
_stickersKey = 0;
_mapChanged = true;
}
if (_recentHashtagsKey) {
@ -2462,9 +2640,9 @@ namespace Local {
_mapChanged = true;
}
if (data->stickers.isEmpty()) {
data->stickers = _stickersMap;
data->stickers = _stickerImagesMap;
} else {
for (StorageMap::const_iterator i = _stickersMap.cbegin(), e = _stickersMap.cend(); i != e; ++i) {
for (StorageMap::const_iterator i = _stickerImagesMap.cbegin(), e = _stickerImagesMap.cend(); i != e; ++i) {
StorageKey k = i.key();
while (data->stickers.constFind(k) != data->stickers.cend()) {
++k.second;
@ -2472,8 +2650,8 @@ namespace Local {
data->stickers.insert(k, i.value());
}
}
if (!_stickersMap.isEmpty()) {
_stickersMap.clear();
if (!_stickerImagesMap.isEmpty()) {
_stickerImagesMap.clear();
_storageStickersSize = 0;
_mapChanged = true;
}

View File

@ -122,8 +122,8 @@ namespace Local {
int32 hasImages();
qint64 storageImagesSize();
void writeSticker(const StorageKey &location, const QByteArray &data, bool overwrite = true);
QByteArray readSticker(const StorageKey &location);
void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true);
QByteArray readStickerImage(const StorageKey &location);
int32 hasStickers();
qint64 storageStickersSize();
@ -132,8 +132,8 @@ namespace Local {
int32 hasAudios();
qint64 storageAudiosSize();
void writeRecentStickers();
void readRecentStickers();
void writeStickers();
void readStickers();
void writeBackground(int32 id, const QImage &img);
bool readBackground();

View File

@ -25,6 +25,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "settingswidget.h"
#include "mainwidget.h"
#include "boxes/confirmbox.h"
#include "boxes/stickersetbox.h"
#include "localstorage.h"
@ -549,6 +550,10 @@ void MainWidget::updateMutedIn(int32 seconds) {
_updateMutedTimer.start(ms);
}
void MainWidget::updateStickers() {
history.updateStickers();
}
void MainWidget::onUpdateMuted() {
App::updateMuted();
}
@ -977,7 +982,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
}
}
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
Local::readRecentStickers();
Local::readRecentHashtags();
recent = cRecentWriteHashtags();
}
found = true;
@ -2473,7 +2478,7 @@ void MainWidget::start(const MTPUser &user) {
}
_started = true;
App::wnd()->sendServiceHistoryRequest();
Local::readRecentStickers();
Local::readStickers();
history.start();
}
@ -2493,6 +2498,11 @@ void MainWidget::openLocalUrl(const QString &url) {
if (m.hasMatch()) {
joinGroupByHash(m.captured(1));
}
} else if (u.startsWith(QLatin1String("tg://addstickers"), Qt::CaseInsensitive)) {
QRegularExpressionMatch m = QRegularExpression(qsl("^tg://addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)$"), QRegularExpression::CaseInsensitiveOption).match(u);
if (m.hasMatch()) {
stickersBox(MTP_inputStickerSetShortName(MTP_string(m.captured(1))));
}
}
}
@ -2513,6 +2523,17 @@ void MainWidget::joinGroupByHash(const QString &hash) {
MTP::send(MTPmessages_CheckChatInvite(MTP_string(hash)), rpcDone(&MainWidget::inviteCheckDone, hash), rpcFail(&MainWidget::inviteCheckFail));
}
void MainWidget::stickersBox(const MTPInputStickerSet &set) {
StickerSetBox *box = new StickerSetBox(set);
connect(box, SIGNAL(installed(uint64)), this, SLOT(onStickersInstalled(uint64)));
App::wnd()->showLayer(box);
}
void MainWidget::onStickersInstalled(uint64 setId) {
emit stickersUpdated();
history.stickersInstalled(setId);
}
void MainWidget::usernameResolveDone(bool toProfile, const MTPUser &user) {
App::wnd()->hideLayer();
UserData *u = App::feedUsers(MTP_vector<MTPUser>(1, user));
@ -2733,28 +2754,24 @@ void MainWidget::updateNotifySetting(PeerData *peer, bool enabled) {
}
void MainWidget::incrementSticker(DocumentData *sticker) {
RecentStickerPack recent(cRecentStickers());
if (!sticker || !sticker->sticker) return;
RecentStickerPack &recent(cGetRecentStickers());
RecentStickerPack::iterator i = recent.begin(), e = recent.end();
for (; i != e; ++i) {
if (i->first == sticker) {
if (i->second > 0) {
++i->second;
} else {
--i->second;
}
if (qAbs(i->second) > 0x4000) {
++i->second;
if (i->second > 0x8000) {
for (RecentStickerPack::iterator j = recent.begin(); j != e; ++j) {
if (qAbs(j->second) > 1) {
if (j->second > 1) {
j->second /= 2;
} else if (j->second > 0) {
j->second = 1;
} else {
j->second = -1;
j->second = 1;
}
}
}
for (; i != recent.begin(); --i) {
if (qAbs((i - 1)->second) > qAbs(i->second)) {
if ((i - 1)->second > i->second) {
break;
}
qSwap(*i, *(i - 1));
@ -2763,11 +2780,45 @@ void MainWidget::incrementSticker(DocumentData *sticker) {
}
}
if (i == e) {
recent.push_front(qMakePair(sticker, -(recent.isEmpty() ? 1 : qAbs(recent.front().second))));
while (recent.size() >= StickerPanPerRow * StickerPanRowsPerPage) recent.pop_back();
recent.push_back(qMakePair(sticker, 1));
for (i = recent.end() - 1; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
qSwap(*i, *(i - 1));
}
}
cSetRecentStickers(recent);
Local::writeRecentStickers();
Local::writeUserSettings();
bool found = false;
uint64 setId = 0;
QString setName;
switch (sticker->sticker->set.type()) {
case mtpc_inputStickerSetID: setId = sticker->sticker->set.c_inputStickerSetID().vid.v; break;
case mtpc_inputStickerSetShortName: setName = qs(sticker->sticker->set.c_inputStickerSetShortName().vshort_name).toLower().trimmed(); break;
}
StickerSets &sets(cRefStickerSets());
for (StickerSets::const_iterator i = sets.cbegin(); i != sets.cend(); ++i) {
if (i->id == CustomStickerSetId || (setId && i->id == setId) || (!setName.isEmpty() && i->shortName.toLower().trimmed() == setName) || (!setId && setName.isEmpty() && i->id == DefaultStickerSetId)) {
for (int32 j = 0, l = i->stickers.size(); j < l; ++j) {
if (i->stickers.at(j) == sticker) {
found = true;
break;
}
}
if (found) break;
}
}
if (!found) {
StickerSets::iterator it = sets.find(CustomStickerSetId);
if (it == sets.cend()) {
it = sets.insert(CustomStickerSetId, StickerSet(CustomStickerSetId, 0, lang(lng_custom_stickers), QString()));
}
it->stickers.push_back(sticker);
Local::writeStickers();
}
history.updateRecentStickers();
}

View File

@ -185,9 +185,12 @@ public:
bool animStep(float64 ms);
void start(const MTPUser &user);
void openLocalUrl(const QString &str);
void openUserByName(const QString &name, bool toProfile = false);
void joinGroupByHash(const QString &hash);
void stickersBox(const MTPInputStickerSet &set);
void startFull(const MTPVector<MTPUser> &users);
bool started();
void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0);
@ -341,6 +344,8 @@ public:
void webPageUpdated(WebPageData *page);
void updateMutedIn(int32 seconds);
void updateStickers();
~MainWidget();
signals:
@ -352,6 +357,7 @@ signals:
void dialogToTop(const History::DialogLinks &links);
void dialogsUpdated();
void showPeerAsync(quint64 peer, qint32 msgId, bool back, bool force);
void stickersUpdated();
public slots:
@ -402,6 +408,8 @@ public slots:
void onUpdateMuted();
void onStickersInstalled(uint64 setId);
private:
void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result);

View File

@ -44,7 +44,7 @@ namespace {
LoaderQueues queues;
}
mtpFileLoader::mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size) : prev(0), next(0),
mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &volume, int32 local, const uint64 &secret, int32 size) : prev(0), next(0),
priority(0), inQueue(false), complete(false), triedLocal(false), skippedBytes(0), nextRequestOffset(0), lastComplete(false),
dc(dc), locationType(0), volume(volume), local(local), secret(secret),
id(0), access(0), fileIsOpen(false), size(size), type(mtpc_storage_fileUnknown) {
@ -247,23 +247,24 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
psPostprocessFile(QFileInfo(file).absoluteFilePath());
}
removeFromQueue();
App::wnd()->update();
App::wnd()->notifyUpdateAllPhotos();
emit App::wnd()->imageLoaded();
if (!queue->queries) {
App::app()->killDownloadSessionsStart(dc);
}
if (!locationType && triedLocal && (fname.isEmpty() || duplicateInData)) {
Local::writeImage(storageKey(dc, volume, local), StorageImageSaved(type, data));
Local::writeImage(storageKey(dc, volume, local), StorageImageSaved(mtpToStorageType(type), data));
} else if (locationType && triedLocal) {
if (!fname.isEmpty()) {
Local::writeFileLocation(mediaKey(locationType, dc, id), FileLocation(type, fname));
Local::writeFileLocation(mediaKey(mtpToLocationType(locationType), dc, id), FileLocation(mtpToStorageType(type), fname));
}
if (duplicateInData) {
if (locationType == mtpc_inputDocumentFileLocation) {
Local::writeSticker(mediaKey(locationType, dc, id), data);
Local::writeStickerImage(mediaKey(mtpToLocationType(locationType), dc, id), data);
} else if (locationType == mtpc_inputAudioFileLocation) {
Local::writeAudio(mediaKey(locationType, dc, id), data);
Local::writeAudio(mediaKey(mtpToLocationType(locationType), dc, id), data);
}
}
}
@ -308,9 +309,9 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
if (!locationType) {
triedLocal = true;
StorageImageSaved cached = Local::readImage(storageKey(dc, volume, local));
if (cached.type != mtpc_storage_fileUnknown) {
if (cached.type != StorageFileUnknown) {
data = cached.data;
type = cached.type;
type = mtpFromStorageType(cached.type);
}
} else if (locationType) {
if (!fname.isEmpty()) {
@ -319,11 +320,11 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
if (duplicateInData) {
if (locationType == mtpc_inputDocumentFileLocation) {
triedLocal = true;
data = Local::readSticker(mediaKey(locationType, dc, id));
data = Local::readStickerImage(mediaKey(mtpToLocationType(locationType), dc, id));
if (!data.isEmpty()) type = mtpc_storage_filePartial;
} else if (locationType == mtpc_inputAudioFileLocation) {
triedLocal = true;
data = Local::readAudio(mediaKey(locationType, dc, id));
data = Local::readAudio(mediaKey(mtpToLocationType(locationType), dc, id));
if (!data.isEmpty()) type = mtpc_storage_filePartial;
}
}
@ -344,8 +345,7 @@ void mtpFileLoader::start(bool loadFirst, bool prior) {
fileIsOpen = false;
psPostprocessFile(QFileInfo(file).absoluteFilePath());
}
App::wnd()->update();
App::wnd()->notifyUpdateAllPhotos();
emit App::wnd()->imageLoaded();
emit progress(this);
return loadNext();
}

View File

@ -27,7 +27,7 @@ class mtpFileLoader : public QObject, public RPCSender {
public:
mtpFileLoader(int32 dc, const int64 &volume, int32 local, const int64 &secret, int32 size = 0);
mtpFileLoader(int32 dc, const uint64 &volume, int32 local, const uint64 &secret, int32 size = 0);
mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size);
mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, mtpTypeId locType, const QString &to, int32 size, bool todata);
bool done() const;
@ -82,9 +82,9 @@ private:
int32 dc;
mtpTypeId locationType; // 0 or mtpc_inputVideoFileLocation / mtpc_inputAudioFileLocation / mtpc_inputDocumentFileLocation
int64 volume; // for photo locations
uint64 volume; // for photo locations
int32 local;
int64 secret;
uint64 secret;
uint64 id; // for other locations
uint64 access;

View File

@ -256,6 +256,7 @@ void ProfileInner::onMediaAudios() {
}
void ProfileInner::onInvitationLink() {
DEBUG_LOG(("Setting text to clipboard from invite url: %1").arg(_peerChat->invitationUrl));
QApplication::clipboard()->setText(_peerChat->invitationUrl);
App::wnd()->showLayer(new ConfirmBox(lang(lng_group_invite_copied), true));
}
@ -769,10 +770,12 @@ void ProfileInner::onMenuDestroy(QObject *obj) {
}
void ProfileInner::onCopyPhone() {
DEBUG_LOG(("Setting text to clipboard from user phone: %1").arg(_phoneText));
QApplication::clipboard()->setText(_phoneText);
}
void ProfileInner::onCopyUsername() {
DEBUG_LOG(("Setting text to clipboard from username: @%1").arg(_peerUser->username));
QApplication::clipboard()->setText('@' + _peerUser->username);
}

View File

@ -89,12 +89,15 @@ RecentEmojiPack gRecentEmojis;
RecentEmojisPreload gRecentEmojisPreload;
EmojiColorVariants gEmojiVariants;
AllStickers gStickers;
QByteArray gStickersHash;
EmojiStickersMap gEmojiStickers;
RecentStickerPreload gRecentStickersPreload;
RecentStickerPack gRecentStickers;
StickerSets gStickerSets;
uint64 gLastStickersUpdate = 0;
RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags;
@ -249,7 +252,7 @@ RecentEmojiPack &cGetRecentEmojis() {
0xD83DDE15LLU,
};
for (int32 i = 0, s = sizeof(defaultRecent) / sizeof(defaultRecent[0]); i < s; ++i) {
if (r.size() >= EmojiPadPerRow * EmojiPadRowsPerPage) break;
if (r.size() >= EmojiPanPerRow * EmojiPanRowsPerPage) break;
EmojiPtr ep(emojiGet(defaultRecent[i]));
if (!ep || ep == TwoSymbolEmoji) continue;
@ -268,3 +271,20 @@ RecentEmojiPack &cGetRecentEmojis() {
}
return cRefRecentEmojis();
}
RecentStickerPack &cGetRecentStickers() {
if (cRecentStickers().isEmpty() && !cRecentStickersPreload().isEmpty()) {
RecentStickerPreload p(cRecentStickersPreload());
cSetRecentStickersPreload(RecentStickerPreload());
RecentStickerPack &recent(cRefRecentStickers());
recent.reserve(p.size());
for (RecentStickerPreload::const_iterator i = p.cbegin(), e = p.cend(); i != e; ++i) {
DocumentData *doc = App::document(i->first);
if (!doc || !doc->sticker) continue;
recent.push_back(qMakePair(doc, i->second));
}
}
return cRefRecentStickers();
}

View File

@ -183,15 +183,31 @@ RecentEmojiPack &cGetRecentEmojis();
struct DocumentData;
typedef QVector<DocumentData*> StickerPack;
typedef QMap<EmojiPtr, StickerPack> AllStickers;
DeclareSetting(AllStickers, Stickers);
DeclareSetting(QByteArray, StickersHash);
typedef QMap<DocumentData*, EmojiPtr> EmojiStickersMap;
DeclareSetting(EmojiStickersMap, EmojiStickers);
typedef QList<QPair<DocumentData*, int16> > RecentStickerPack;
DeclareSetting(RecentStickerPack, RecentStickers);
typedef QList<QPair<DocumentData*, int16> > RecentStickerPackOld;
typedef QVector<QPair<uint64, ushort> > RecentStickerPreload;
typedef QVector<QPair<DocumentData*, ushort> > RecentStickerPack;
DeclareSetting(RecentStickerPreload, RecentStickersPreload);
DeclareRefSetting(RecentStickerPack, RecentStickers);
RecentStickerPack &cGetRecentStickers();
DeclareSetting(uint64, LastStickersUpdate);
static const uint64 DefaultStickerSetId = 0, CustomStickerSetId = 0xFFFFFFFFFFFFFFFFLLU, RecentStickerSetId = 0xFFFFFFFFFFFFFFFELLU;
struct StickerSet {
StickerSet(uint64 id, uint64 access, const QString &title, const QString &shortName) : id(id), access(access), title(title), shortName(shortName) {
}
uint64 id, access;
QString title, shortName;
StickerPack stickers;
};
typedef QMap<uint64, StickerSet> StickerSets;
DeclareRefSetting(StickerSets, StickerSets);
typedef QList<QPair<QString, ushort> > RecentHashtagPack;
DeclareSetting(RecentHashtagPack, RecentWriteHashtags);

View File

@ -348,7 +348,7 @@ void VideoCancelLink::onClick(Qt::MouseButton button) const {
VideoData::VideoData(const VideoId &id, const uint64 &access, int32 user, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) :
id(id), access(access), user(user), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
location = Local::readFileLocation(mediaKey(mtpc_inputVideoFileLocation, dc, id));
location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id));
}
void VideoData::save(const QString &toFile) {
@ -361,7 +361,7 @@ void VideoData::save(const QString &toFile) {
QString VideoData::already(bool check) {
if (!check) return location.name;
if (!location.check()) location = Local::readFileLocation(mediaKey(mtpc_inputVideoFileLocation, dc, id));
if (!location.check()) location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id));
return location.name;
}
@ -442,7 +442,7 @@ void AudioCancelLink::onClick(Qt::MouseButton button) const {
AudioData::AudioData(const AudioId &id, const uint64 &access, int32 user, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) :
id(id), access(access), user(user), date(date), mime(mime), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) {
location = Local::readFileLocation(mediaKey(mtpc_inputAudioFileLocation, dc, id));
location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id));
}
void AudioData::save(const QString &toFile) {
@ -455,7 +455,7 @@ void AudioData::save(const QString &toFile) {
QString AudioData::already(bool check) {
if (!check) return location.name;
if (!location.check()) location = Local::readFileLocation(mediaKey(mtpc_inputAudioFileLocation, dc, id));
if (!location.check()) location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id));
return location.name;
}
@ -560,7 +560,7 @@ void DocumentCancelLink::onClick(Qt::MouseButton button) const {
DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) :
id(id), type(FileDocument), duration(0), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0), sticker(0) {
setattributes(attributes);
location = Local::readFileLocation(mediaKey(mtpc_inputDocumentFileLocation, dc, id));
location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id));
}
void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) {
@ -607,7 +607,7 @@ void DocumentData::save(const QString &toFile) {
QString DocumentData::already(bool check) {
if (!check) return location.name;
if (!location.check()) location = Local::readFileLocation(mediaKey(mtpc_inputDocumentFileLocation, dc, id));
if (!location.check()) location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id));
return location.name;
}

View File

@ -253,7 +253,7 @@ struct VideoData {
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName());
}
loader->deleteLater();
loader->rpcInvalidate();
@ -339,7 +339,7 @@ struct AudioData {
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName());
data = loader->bytes();
}
loader->deleteLater();
@ -409,6 +409,7 @@ struct StickerData {
QString alt;
MTPInputStickerSet set;
StorageImageLocation loc; // doc thumb location
};
enum DocumentType {
@ -446,7 +447,7 @@ struct DocumentData {
void finish() {
if (loader->done()) {
location = FileLocation(loader->fileType(), loader->fileName());
location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName());
data = loader->bytes();
}
loader->deleteLater();

View File

@ -225,51 +225,52 @@ QString translitRusEng(const QString &rus);
QString rusKeyboardLayoutSwitch(const QString &from);
enum DataBlockId {
dbiKey = 0,
dbiUser = 1,
dbiDcOption = 2,
dbiMaxGroupCount = 3,
dbiMutePeer = 4,
dbiSendKey = 5,
dbiAutoStart = 6,
dbiStartMinimized = 7,
dbiSoundNotify = 8,
dbiWorkMode = 9,
dbiSeenTrayTooltip = 10,
dbiDesktopNotify = 11,
dbiAutoUpdate = 12,
dbiLastUpdateCheck = 13,
dbiWindowPosition = 14,
dbiConnectionType = 15,
dbiKey = 0x00,
dbiUser = 0x01,
dbiDcOption = 0x02,
dbiMaxGroupCount = 0x03,
dbiMutePeer = 0x04,
dbiSendKey = 0x05,
dbiAutoStart = 0x06,
dbiStartMinimized = 0x07,
dbiSoundNotify = 0x08,
dbiWorkMode = 0x09,
dbiSeenTrayTooltip = 0x0a,
dbiDesktopNotify = 0x0b,
dbiAutoUpdate = 0x0c,
dbiLastUpdateCheck = 0x0d,
dbiWindowPosition = 0x0e,
dbiConnectionType = 0x0f,
// 16 reserved
dbiDefaultAttach = 17,
dbiCatsAndDogs = 18,
dbiReplaceEmojis = 19,
dbiAskDownloadPath = 20,
dbiDownloadPath = 21,
dbiScale = 22,
dbiEmojiTab = 23,
dbiRecentEmojisOld = 24,
dbiLoggedPhoneNumber = 25,
dbiMutedPeers = 26,
dbiDefaultAttach = 0x11,
dbiCatsAndDogs = 0x12,
dbiReplaceEmojis = 0x13,
dbiAskDownloadPath = 0x14,
dbiDownloadPath = 0x15,
dbiScale = 0x16,
dbiEmojiTab = 0x17,
dbiRecentEmojisOld = 0x18,
dbiLoggedPhoneNumber = 0x19,
dbiMutedPeers = 0x1a,
// 27 reserved
dbiNotifyView = 28,
dbiSendToMenu = 29,
dbiCompressPastedImage = 30,
dbiLang = 31,
dbiLangFile = 32,
dbiTileBackground = 33,
dbiAutoLock = 34,
dbiDialogLastPath = 35,
dbiRecentEmojis = 36,
dbiEmojiVariants = 37,
dbiNotifyView = 0x1c,
dbiSendToMenu = 0x1d,
dbiCompressPastedImage = 0x1e,
dbiLang = 0x1f,
dbiLangFile = 0x20,
dbiTileBackground = 0x21,
dbiAutoLock = 0x22,
dbiDialogLastPath = 0x23,
dbiRecentEmojis = 0x24,
dbiEmojiVariants = 0x25,
dbiRecentStickers = 0x26,
dbiEncryptedWithSalt = 333,
dbiEncrypted = 444,
dbiEncryptedWithSalt = 333,
dbiEncrypted = 444,
// 500-600 reserved
dbiVersion = 666,
dbiVersion = 666,
};
enum DBISendKey {

View File

@ -381,6 +381,9 @@ _connecting(0), _clearManager(0), dragging(false), _inactivePress(false), _shoul
connect(&_isActiveTimer, SIGNAL(timeout()), this, SLOT(updateIsActive()));
connect(&_autoLockTimer, SIGNAL(timeout()), this, SLOT(checkAutoLock()));
connect(this, SIGNAL(imageLoaded()), this, SLOT(update()));
connect(this, SIGNAL(imageLoaded()), this, SLOT(notifyUpdateAllPhotos()));
}
void Window::inactivePress(bool inactive) {
@ -609,7 +612,7 @@ void Window::sendServiceHistoryRequest() {
}
void Window::setupMain(bool anim, const MTPUser *self) {
Local::readRecentStickers();
Local::readStickers();
QPixmap bg = anim ? myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)) : QPixmap();
clearWidgets();

View File

@ -219,7 +219,6 @@ public:
void notifyItemRemoved(HistoryItem *item);
void notifyStopHiding();
void notifyStartHiding();
void notifyUpdateAllPhotos();
void notifyUpdateAll();
void notifyActivateAll();
@ -270,6 +269,8 @@ public slots:
QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon);
void notifyUpdateAllPhotos();
signals:
void resized(const QSize &size);
@ -277,6 +278,8 @@ signals:
void tempDirClearFailed(int task);
void newAuthorization();
void imageLoaded();
private:
void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color);

Binary file not shown.

View File

@ -377,6 +377,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_stickersetbox.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_switcher.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -635,6 +639,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_stickersetbox.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_switcher.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -918,6 +926,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_stickersetbox.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_switcher.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@ -966,6 +978,7 @@
<ClCompile Include="SourceFiles\boxes\photocropbox.cpp" />
<ClCompile Include="SourceFiles\boxes\photosendbox.cpp" />
<ClCompile Include="SourceFiles\boxes\sessionsbox.cpp" />
<ClCompile Include="SourceFiles\boxes\stickersetbox.cpp" />
<ClCompile Include="SourceFiles\boxes\usernamebox.cpp" />
<ClCompile Include="SourceFiles\dialogswidget.cpp" />
<ClCompile Include="SourceFiles\dropdown.cpp" />
@ -1355,6 +1368,20 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/abstractbox.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
</CustomBuild>
<CustomBuild Include="SourceFiles\boxes\stickersetbox.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing stickersetbox.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/stickersetbox.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing stickersetbox.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/stickersetbox.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing stickersetbox.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/stickersetbox.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
</CustomBuild>
<ClInclude Include="SourceFiles\config.h" />
<CustomBuild Include="SourceFiles\gui\animation.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing animation.h...</Message>

View File

@ -879,6 +879,18 @@
<ClCompile Include="SourceFiles\structs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_stickersetbox.cpp">
<Filter>Generated Files\Deploy</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_stickersetbox.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_stickersetbox.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\boxes\stickersetbox.cpp">
<Filter>boxes</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h">
@ -1168,6 +1180,9 @@
<CustomBuild Include="SourceFiles\intro\intropwdcheck.h">
<Filter>intro</Filter>
</CustomBuild>
<CustomBuild Include="SourceFiles\boxes\stickersetbox.h">
<Filter>boxes</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="SourceFiles\art\icon256.ico" />