Compare commits

...

13 Commits

Author SHA1 Message Date
John Preston
147eaab59a prepared version 0.8.14.dev 2015-05-19 18:48:00 +03:00
John Preston
890ec34202 Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-19 18:46:51 +03:00
John Preston
92858dc7d3 sticker packs done 2015-05-19 18:46:45 +03:00
John Preston
4440b42848 openssl multithread locks support added 2015-05-19 18:42:32 +03:00
John Preston
136fd5c8e1 added lock for dcOptions, emojibox large emoji display, session management improved, new emoji dropdown started 2015-05-14 19:50:04 +03:00
John Preston
d92356ce28 improved emoji text replaces 2015-05-12 18:01:49 +03:00
John Preston
47f673aa69 Merge branch 'master' of https://github.com/telegramdesktop/tdesktop into dev 2015-05-12 17:25:56 +03:00
John Preston
64d4a3e8a8 removed QT_STYLE_OVERRIDE env variable read 2015-05-12 15:03:10 +03:00
John Preston
87e72fc7aa langs updated 2015-05-12 14:53:50 +03:00
John Preston
6d44a3ec95 emoji sprites improved 2015-05-11 16:33:02 +03:00
John Preston
eb47eabba4 langs updated 2015-05-11 16:31:37 +03:00
John Preston
cfb0de69f0 next layer, stickerdata moved from documentdata 2015-05-11 15:44:27 +03:00
John Preston
2d46cc4c11 preparing version 0.8.13 with new emoji pan 2015-05-11 13:18:57 +03:00
78 changed files with 3626 additions and 1133 deletions

View File

@@ -1,9 +1,9 @@
@echo OFF
set "AppVersion=8012"
set "AppVersionStrSmall=0.8.12"
set "AppVersionStr=0.8.12"
set "AppVersionStrFull=0.8.12.0"
set "AppVersion=8014"
set "AppVersionStrSmall=0.8.14"
set "AppVersionStr=0.8.14"
set "AppVersionStrFull=0.8.14.0"
set "DevChannel=1"
if %DevChannel% neq 0 goto preparedev

View File

@@ -389,16 +389,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_changed_title" = "{from} changed group name to «{title}»";
"lng_action_created_chat" = "{from} created group «{title}»";
"lng_group_invite_bad_link" = "This invite link is broken\nor it has expired.";
"lng_group_invite_bad_link" = "This invite link is broken\nor has expired.";
"lng_group_invite_want_join" = "Do you want to join the group «{title}»?";
"lng_group_invite_join" = "Join";
"lng_group_invite_link" = "Invite link";
"lng_group_invite_create" = "Create an invite link";
"lng_group_invite_about" = "You can create a link for joining this group.\nAnyone who has that link can join.";
"lng_group_invite_create_new" = "Create new link";
"lng_group_invite_about_new" = "Current invite link will stop working\nwhen you create a new one.";
"lng_group_invite_copied" = "Invite link was copied to clipboard.";
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
"lng_group_invite_create_new" = "Revoke invite link";
"lng_group_invite_about_new" = "Your previous link will be deactivated\nand we'll generate a new invite link for you.";
"lng_group_invite_copied" = "Invite link copied to clipboard.";
"lng_group_invite_no_room" = "Unable to join this group because there are\ntoo many members in it already.";
"lng_forwarded_from" = "Forwarded from";
@@ -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";
@@ -573,7 +583,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
"lng_new_version_text" = "— Invite links for group chats\n— Gray unread badge for muted conversations";
"lng_new_version_text" = "— Added support for new emoji\n— Improved emoji and stickers panel";
"lng_menu_insert_unicode" = "Insert Unicode control character";

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;
@@ -1362,7 +1364,7 @@ btnInfoClose: flatButton(aboutCloseButton) {
emojiTextFont: font(16px);
emojiReplaceWidth: 56px;
emojiReplaceHeight: 56px;
emojiReplaceInnerHeight: 38px;
emojiReplaceInnerHeight: 42px;
connectingBG: #fffe;
connectingColor: #777;
@@ -1452,47 +1454,90 @@ 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;
}
emojiRecent: sprite(6px, 197px, 20px, 20px);
emojiRecentOver: sprite(290px, 221px, 20px, 20px);
emojiRecentActive: sprite(290px, 242px, 20px, 20px);
emojiPeople: sprite(27px, 197px, 20px, 20px);
emojiPeopleOver: sprite(311px, 221px, 20px, 20px);
emojiPeopleActive: sprite(311px, 242px, 20px, 20px);
emojiNature: sprite(48px, 197px, 20px, 20px);
emojiNatureOver: sprite(245px, 266px, 20px, 20px);
emojiNatureActive: sprite(245px, 287px, 20px, 20px);
emojiFood: sprite(69px, 197px, 20px, 20px);
emojiFoodOver: sprite(266px, 266px, 20px, 20px);
emojiFoodActive: sprite(266px, 287px, 20px, 20px);
emojiCelebration: sprite(90px, 197px, 20px, 20px);
emojiCelebrationOver: sprite(290px, 266px, 20px, 20px);
emojiCelebrationActive: sprite(290px, 287px, 20px, 20px);
emojiActivity: sprite(111px, 197px, 20px, 20px);
emojiActivityOver: sprite(311px, 266px, 20px, 20px);
emojiActivityActive: sprite(311px, 287px, 20px, 20px);
emojiTravel: sprite(132px, 197px, 20px, 20px);
emojiTravelOver: sprite(321px, 344px, 20px, 20px);
emojiTravelActive: sprite(321px, 365px, 20px, 20px);
emojiObjects: sprite(153px, 197px, 20px, 20px);
emojiObjectsOver: sprite(342px, 344px, 20px, 20px);
emojiObjectsActive: sprite(342px, 365px, 20px, 20px);
emojiStickers: sprite(174px, 197px, 20px, 20px);
emojiStickersOver: sprite(363px, 344px, 20px, 20px);
emojiStickersActive: sprite(363px, 365px, 20px, 20px);
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(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);
emojiFood: sprite(63px, 196px, 21px, 22px);
emojiFoodOver: sprite(266px, 264px, 21px, 22px);
emojiFoodActive: sprite(266px, 286px, 21px, 22px);
emojiCelebration: sprite(84px, 196px, 21px, 22px);
emojiCelebrationOver: sprite(287px, 264px, 21px, 22px);
emojiCelebrationActive: sprite(287px, 286px, 21px, 22px);
emojiActivity: sprite(105px, 196px, 21px, 22px);
emojiActivityOver: sprite(308px, 264px, 21px, 22px);
emojiActivityActive: sprite(308px, 286px, 21px, 22px);
emojiTravel: sprite(126px, 196px, 21px, 22px);
emojiTravelOver: sprite(321px, 344px, 21px, 22px);
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: 28px;
height: 36px;
width: 36px;
height: 46px;
textTop: 0px;
textLeft: 0px;
@@ -1502,7 +1547,7 @@ rbEmoji: flatCheckbox {
cursor: cursor(pointer);
disabledCursor: cursor(default);
imagePos: point(5px, 8px);
imagePos: point(8px, 12px);
}
rbEmojiRecent: flatCheckbox(rbEmoji) {
imageRect: emojiRecent;
@@ -1568,33 +1613,33 @@ rbEmojiObjects: flatCheckbox(rbEmoji) {
disImageRect: emojiObjects;
chkDisImageRect: emojiObjectsActive;
}
rbEmojiStickers: flatCheckbox(rbEmojiRecent) {
imageRect: emojiStickers;
chkImageRect: emojiStickersActive;
overImageRect: emojiStickersOver;
chkOverImageRect: emojiStickersActive;
disImageRect: emojiStickers;
chkDisImageRect: emojiStickersActive;
}
emojiPanPadding: margins(5px, 0px, 0px, 5px);
emojiPanSize: size(35px, 35px);
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;
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

@@ -96,9 +96,9 @@ const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace);
typedef QMap<QString, uint32> ReplaceMap;
ReplaceMap replaceMap;
static const int variantsCount = 5, inRow = 40, imSizes[] = { 18, 22, 27, 36, 45 };
static const int emojiFontSizes[] = { 14, 20, 27, 36, 45 };
static const int emojiDeltas[] = { 15, 20, 25, 34, 42 };
static const int variantsCount = 5, inRow = 40, imSizes[] = { 18, 22, 27, 36, 45, 180 }, badSizes[] = { 1, 1, 0, 0, 0 };
static const int emojiFontSizes[] = { 14, 20, 27, 36, 45, 180 };
static const int emojiDeltas[] = { 15, 20, 25, 34, 42, 167 };
static const char *variantPostfix[] = { "", "_125x", "_150x", "_200x", "_250x" };
static const char *variantNames[] = { "dbisOne", "dbisOneAndQuarter", "dbisOneAndHalf", "dbisTwo" };
@@ -1382,23 +1382,32 @@ bool genEmoji(QString, const QString &emoji_out, const QString &emoji_png) {
QStringList str = QFontDatabase::applicationFontFamilies(QFontDatabase::addApplicationFont(QStringLiteral("/System/Library/Fonts/Apple Color Emoji.ttf")));
for (int variantIndex = 0; variantIndex < variantsCount; variantIndex++) {
int imSize = imSizes[variantIndex];
int imSize = imSizes[variantIndex], bad = badSizes[variantIndex], badSize = (bad ? imSizes[5] : imSize);
QFont f(QGuiApplication::font());
f.setFamily(QStringLiteral("Apple Color Emoji"));
f.setPixelSize(emojiFontSizes[variantIndex]);
f.setPixelSize(emojiFontSizes[bad ? 5 : variantIndex]);
int s = 4 + imSize;
int s = 4 + badSize;
QImage badImg(inRow * badSize, currentRow * badSize, QImage::Format_ARGB32);
QImage emojisImg(inRow * imSize, currentRow * imSize, QImage::Format_ARGB32), emojiImg(s, s, QImage::Format_ARGB32);
{
QPainter p(&emojisImg);
QPainter p(&emojisImg), q(&badImg);
QPainter::CompositionMode m = p.compositionMode();
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(0, 0, emojisImg.width(), emojisImg.height(), Qt::transparent);
p.setCompositionMode(m);
p.setRenderHint(QPainter::SmoothPixmapTransform);
if (bad) {
QPainter::CompositionMode m = q.compositionMode();
q.setCompositionMode(QPainter::CompositionMode_Source);
q.fillRect(0, 0, emojisImg.width(), emojisImg.height(), Qt::transparent);
q.setCompositionMode(m);
q.setRenderHint(QPainter::SmoothPixmapTransform);
}
for (EmojisData::const_iterator it = emojisData.cbegin(), e = emojisData.cend(); it != e; ++it) {
QRect drawFrom(2, 2, imSize, imSize);
QRect drawFrom(2, 2, badSize, badSize);
QString es = textEmojiString(&it.value());
{
QPainter q(&emojiImg);
q.setPen(QColor(0, 0, 0, 255));
@@ -1407,7 +1416,7 @@ bool genEmoji(QString, const QString &emoji_out, const QString &emoji_png) {
q.setCompositionMode(QPainter::CompositionMode_Source);
q.fillRect(0, 0, emojiImg.width(), emojiImg.height(), Qt::transparent);
q.setCompositionMode(m);
q.drawText(2, 2 + emojiDeltas[variantIndex], textEmojiString(&it.value()));
q.drawText(2, 2 + emojiDeltas[bad ? 5 : variantIndex], es);
}
int top = 1, bottom = 1, left = 1, right = 1;
QRgb *b = (QRgb*)emojiImg.bits();
@@ -1455,10 +1464,17 @@ bool genEmoji(QString, const QString &emoji_out, const QString &emoji_png) {
drawFrom.setX(drawFrom.x() - 1);
}
}
p.drawImage(QRect(it->x * imSize, it->y * imSize, imSize, imSize), emojiImg, drawFrom);
if (bad) {
p.drawImage(QRect(it->x * imSize, it->y * imSize, imSize, imSize), emojiImg.copy(2, 2, badSize, badSize).scaled(imSize, imSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
q.drawImage(QRect(it->x * badSize, it->y * badSize, badSize, badSize), emojiImg, QRect(2, 2, badSize, badSize));
} else {
p.drawImage(QRect(it->x * imSize, it->y * imSize, imSize, imSize), emojiImg, drawFrom);
}
}
}
QString postfix = variantPostfix[variantIndex], emojif = emoji_png + postfix + ".webp";
// emojisImg.save(emoji_png + postfix + ".png");
// if (bad) badImg.save(emoji_png + "_bad.png");
QByteArray emojib;
{
QBuffer ebuf(&emojib);

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->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->alt = qs(d.valt);
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,9 +662,9 @@ 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));
} else if (!DevChannel && Local::oldMapVersion() < 8011) {
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();
}
if (!versionFeatures.isEmpty()) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 KiB

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 KiB

After

Width:  |  Height:  |  Size: 789 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 215 KiB

View File

@@ -68,12 +68,12 @@ namespace {
const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace), replacesInRow = 7;
}
EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton) {
EmojiBox::EmojiBox() : _esize(EmojiSizes[EIndex + 1]), _done(this, lang(lng_about_done), st::aboutCloseButton) {
fillBlocks();
_blockHeight = st::emojiReplaceInnerHeight;
resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - st::emojiSize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height());
resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - _esize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height());
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
@@ -84,7 +84,21 @@ void EmojiBox::fillBlocks() {
BlockRow currentRow;
currentRow.reserve(replacesInRow);
for (uint32 i = 0; i < replacesCount; ++i) {
Block block(emojiGet(replaces[i].code), QString::fromUtf8(replaces[i].replace));
EmojiPtr emoji = emojiGet(replaces[i].code);
if (!emoji || emoji == TwoSymbolEmoji) continue;
if (emoji->color) {
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
if (it != cEmojiVariants().cend()) {
EmojiPtr replace = emojiFromKey(it.value());
if (replace) {
if (replace != TwoSymbolEmoji && replace->code == emoji->code && replace->code2 == emoji->code2) {
emoji = replace;
}
}
}
}
Block block(emoji, QString::fromUtf8(replaces[i].replace));
currentRow.push_back(block);
if (uint32(currentRow.size()) == replacesInRow) {
_blocks.push_back(currentRow);
@@ -125,7 +139,7 @@ void EmojiBox::paintEvent(QPaintEvent *e) {
int32 rowSize = i->size(), left = (width() - rowSize * st::emojiReplaceWidth) / 2;
for (BlockRow::const_iterator j = i->cbegin(), en = i->cend(); j != en; ++j) {
if (j->emoji) {
emojiDraw(p, j->emoji, left + (st::emojiReplaceWidth - st::emojiSize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2);
p.drawPixmap(QPoint(left + (st::emojiReplaceWidth - _esize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2), App::emojisLarge(), QRect(j->emoji->x * _esize, j->emoji->y * _esize, _esize, _esize));
}
QRect trect(left, top + (st::emojiReplaceHeight + _blockHeight) / 2 - st::emojiTextFont->height, st::emojiReplaceWidth, st::emojiTextFont->height);
p.drawText(trect, j->text, QTextOption(Qt::AlignHCenter | Qt::AlignTop));

View File

@@ -38,6 +38,7 @@ private:
void fillBlocks();
int32 _esize;
BottomButton _done;
int32 _blockHeight;

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,8 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
static const int32 AppVersion = 8012;
static const wchar_t *AppVersionStr = L"0.8.12";
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)";
@@ -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

@@ -26,6 +26,31 @@ EmojiPtr emojiGet(EmojiPtr emoji, uint32 color);
EmojiPtr emojiGet(const QChar *from, const QChar *end);
QString emojiGetSequence(int index);
inline QString emojiString(EmojiPtr emoji) {
if ((emoji->code & 0xFFFF0000U) == 0xFFFF0000U) { // sequence
return emojiGetSequence(emoji->code & 0xFFFFU);
}
QString result;
result.reserve(emoji->len + (emoji->postfix ? 1 : 0));
if (!(emoji->code >> 16)) {
result.append(QChar(emoji->code & 0xFFFF));
} else {
result.append(QChar((emoji->code >> 16) & 0xFFFF));
result.append(QChar(emoji->code & 0xFFFF));
if (emoji->code2) {
result.append(QChar((emoji->code2 >> 16) & 0xFFFF));
result.append(QChar(emoji->code2 & 0xFFFF));
}
}
if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
result.append(QChar((emoji->color >> 16) & 0xFFFF));
result.append(QChar(emoji->color & 0xFFFF));
}
if (emoji->postfix) result.append(QChar(emoji->postfix));
return result;
}
inline uint64 emojiKey(EmojiPtr emoji) {
uint64 key = emoji->code;
if (emoji->code2) {
@@ -138,7 +163,8 @@ inline QString replaceEmojis(const QString &text) {
while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) {
++currentLink;
}
if (emojiCode &&
EmojiPtr emoji = emojiCode ? emojiGet(emojiCode) : 0;
if (emoji && emoji != TwoSymbolEmoji &&
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
(newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
(currentLink >= lnkCount || (ch < lnkRanges[currentLink].from && newEmojiEnd <= lnkRanges[currentLink].from) || (ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len && newEmojiEnd > lnkRanges[currentLink].from + lnkRanges[currentLink].len))
@@ -148,10 +174,18 @@ inline QString replaceEmojis(const QString &text) {
if (ch > emojiEnd + (consumePrevious ? 1 : 0)) {
result.append(emojiEnd, ch - emojiEnd - (consumePrevious ? 1 : 0));
}
if (emojiCode > 65535) {
result.append(QChar((emojiCode >> 16) & 0xFFFF));
if (emoji->color) {
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
if (it != cEmojiVariants().cend()) {
EmojiPtr replace = emojiFromKey(it.value());
if (replace) {
if (replace != TwoSymbolEmoji && replace->code == emoji->code && replace->code2 == emoji->code2) {
emoji = replace;
}
}
}
}
result.append(QChar(emojiCode & 0xFFFF));
result.append(emojiString(emoji));
ch = emojiEnd = newEmojiEnd;
canFindEmoji = true;

View File

@@ -373,7 +373,7 @@ QString FlatTextarea::getText(int32 start, int32 end) const {
QString imageName = static_cast<QTextImageFormat*>(&f)->name();
if (imageName.startsWith(QLatin1String("emoji://e."))) {
if (EmojiPtr emoji = emojiFromUrl(imageName)) {
emojiText = textEmojiString(emoji);
emojiText = emojiString(emoji);
}
}
}

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

@@ -239,31 +239,6 @@ const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink)
return (result < end && *result == TextCommand) ? (result + 1) : from;
}
QString textEmojiString(EmojiPtr emoji) {
if ((emoji->code & 0xFFFF0000U) == 0xFFFF0000U) { // sequence
return emojiGetSequence(emoji->code & 0xFFFFU);
}
QString result;
result.reserve(emoji->len + (emoji->postfix ? 1 : 0));
if (!(emoji->code >> 16)) {
result.append(QChar(emoji->code & 0xFFFF));
} else {
result.append(QChar((emoji->code >> 16) & 0xFFFF));
result.append(QChar(emoji->code & 0xFFFF));
if (emoji->code2) {
result.append(QChar((emoji->code2 >> 16) & 0xFFFF));
result.append(QChar(emoji->code2 & 0xFFFF));
}
}
if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
result.append(QChar((emoji->color >> 16) & 0xFFFF));
result.append(QChar(emoji->color & 0xFFFF));
}
if (emoji->postfix) result.append(QChar(emoji->postfix));
return result;
}
class TextParser {
public:
@@ -527,6 +502,10 @@ public:
for (int l = len - skipped - 1; l > 0; --l) {
_t->_text.push_back(*++ptr);
}
if (e->postfix && _t->_text.at(_t->_text.size() - 1).unicode() != e->postfix) {
_t->_text.push_back(e->postfix);
++len;
}
createBlock(-len);
emoji = e;
@@ -757,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

@@ -534,8 +534,6 @@ QString textcmdStartColor(const style::color &color);
QString textcmdStopColor();
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true);
QString textEmojiString(EmojiPtr emoji);
inline bool chIsSpace(QChar ch, bool rich = false) {
return ch.isSpace() || (ch < 32 && !(rich && ch == TextCommand)) || (ch == QChar::ParagraphSeparator) || (ch == QChar::LineSeparator) || (ch == QChar::ObjectReplacementCharacter) || (ch == QChar::SoftHyphen) || (ch == QChar::CarriageReturn) || (ch == QChar::Tabulation);
}
@@ -546,10 +544,11 @@ inline bool chIsTrimmed(QChar ch, bool rich = false) {
return (!rich || ch != TextCommand) && (chIsSpace(ch) || chIsBad(ch));
}
inline bool chIsDiac(QChar ch) { // diac and variation selectors
return (ch >= 768 && ch < 880) || (ch >= 7616 && ch < 7680) || (ch >= 8400 && ch < 8448) || (ch >= 65056 && ch < 65072);
QChar::Category c = ch.category();
return (c == QChar::Mark_NonSpacing);
}
inline int32 chMaxDiacAfterSymbol() {
return 4;
return 2;
}
inline bool chIsNewline(QChar ch) {
return (ch == QChar::LineFeed || ch == 156);

View File

@@ -2894,13 +2894,13 @@ HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia()
}
bool HistorySticker::updateStickerEmoji() {
if (!data->alt.isEmpty()) {
_emoji = data->alt;
if (!data->sticker->alt.isEmpty()) {
_emoji = data->sticker->alt;
return true;
}
const EmojiStickersMap &stickers(cEmojiStickers());
EmojiStickersMap::const_iterator i = stickers.constFind(data);
QString emoji = (i == stickers.cend()) ? QString() : textEmojiString(i.value());
QString emoji = (i == stickers.cend()) ? QString() : emojiString(i.value());
if (emoji != _emoji) {
_emoji = emoji;
return true;
@@ -2948,24 +2948,24 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected,
if (!data->loader && data->status != FileFailed && !already && !hasdata) {
data->save(QString());
}
if (data->sticker->isNull() && (already || hasdata)) {
if (data->sticker->img->isNull() && (already || hasdata)) {
if (already) {
data->sticker = ImagePtr(data->already());
data->sticker->img = ImagePtr(data->already());
} else {
data->sticker = ImagePtr(data->data);
data->sticker->img = ImagePtr(data->data);
}
}
if (selected) {
if (data->sticker->isNull()) {
if (data->sticker->img->isNull()) {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurredColored(st::msgStickerOverlay, pixw, pixh));
} else {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker->pixColored(st::msgStickerOverlay, pixw, pixh));
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker->img->pixColored(st::msgStickerOverlay, pixw, pixh));
}
} else {
if (data->sticker->isNull()) {
if (data->sticker->img->isNull()) {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->thumb->pixBlurred(pixw, pixh));
} else {
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker->pix(pixw, pixh));
p.drawPixmap(QPoint(usex + (usew - pixw) / 2, (_minh - pixh) / 2), data->sticker->img->pix(pixw, pixh));
}
}
@@ -4606,7 +4606,7 @@ void HistoryMessage::initMediaFromText(QString &currentText) {
}
void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
if (doc->type == StickerDocument && doc->dimensions.width() > 0 && doc->dimensions.height() > 0 && doc->dimensions.width() <= StickerMaxSize && doc->dimensions.height() <= StickerMaxSize && doc->size < StickerInMemory) {
if (doc->type == StickerDocument && doc->sticker && doc->dimensions.width() > 0 && doc->dimensions.height() > 0 && doc->dimensions.width() <= StickerMaxSize && doc->dimensions.height() <= StickerMaxSize && doc->size < StickerInMemory) {
_media = new HistorySticker(doc);
} else {
_media = new HistoryDocument(doc);
@@ -4879,7 +4879,7 @@ void HistoryMessage::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y)
lnk = _from->lnk;
return;
}
// width -= st::msgPhotoSkip;
// width -= st::msgPhotoSkip;
left += st::msgPhotoSkip;
}
if (width < 1) return;
@@ -4900,6 +4900,10 @@ void HistoryMessage::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y)
}
r.setTop(r.top() + st::msgNameFont->height);
}
return getStateFromMessageText(lnk, inText, x, y, r);
}
void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const {
QRect trect(r.marginsAdded(-st::msgPadding));
TextLinkPtr medialnk;
if (_media) {
@@ -5056,12 +5060,10 @@ void HistoryForwarded::drawForwardedFrom(QPainter &p, int32 x, int32 y, int32 w,
}
void HistoryForwarded::drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const {
int32 h = st::msgServiceNameFont->height;
drawForwardedFrom(p, trect.x(), trect.y(), trect.width(), (selection == FullItemSel));
QRect realtrect(trect);
realtrect.setY(trect.y() + h);
realtrect.setY(trect.y() + st::msgServiceNameFont->height);
HistoryMessage::drawMessageText(p, realtrect, selection);
}
@@ -5139,6 +5141,12 @@ void HistoryForwarded::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y
return HistoryMessage::getState(lnk, inText, x, y);
}
void HistoryForwarded::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const {
QRect realr(r);
realr.setHeight(r.height() - st::msgServiceNameFont->height);
HistoryMessage::getStateFromMessageText(lnk, inText, x, y, realr);
}
void HistoryForwarded::getForwardedState(TextLinkPtr &lnk, bool &inText, int32 x, int32 w) const {
inText = false;
if (x >= fromWidth && x < w && x < fromWidth + fwdFromName.maxWidth()) {
@@ -5445,6 +5453,14 @@ void HistoryReply::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) co
return HistoryMessage::getState(lnk, inText, x, y);
}
void HistoryReply::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const {
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
QRect realr(r);
realr.setHeight(r.height() - h);
HistoryMessage::getStateFromMessageText(lnk, inText, x, y, realr);
}
void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const {
symbol = 0;
after = false;

View File

@@ -1238,7 +1238,10 @@ public:
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
bool hasPoint(int32 x, int32 y) const;
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
virtual void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const {
return _text.adjustSelection(from, to, type);
@@ -1311,6 +1314,7 @@ public:
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
bool hasPoint(int32 x, int32 y) const;
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
void getForwardedState(TextLinkPtr &lnk, bool &inText, int32 x, int32 w) const;
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
@@ -1363,6 +1367,7 @@ public:
int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0);
bool hasPoint(int32 x, int32 y) const;
void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const;
void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const;
void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const;
UserData *replyTo() const {

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;
}
@@ -3256,8 +3365,8 @@ namespace {
}
if (document->type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (document->type == StickerDocument) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(document->alt)));
} else if (document->type == StickerDocument && document->sticker) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(document->sticker->alt), document->sticker->set));
}
return MTP_vector<MTPDocumentAttribute>(attributes);
}
@@ -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

@@ -153,6 +153,7 @@ void IntroCode::activate() {
callTimer.start(1000);
error = "";
errorAlpha = anim::fvalue(0);
sentCode = QString();
show();
code.setDisabled(false);
code.setFocus();

View File

@@ -389,17 +389,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_changed_title" = "{from} hat den Gruppennamen zu «{title}» geändert";
"lng_action_created_chat" = "{from} hat die Gruppe «{title}» erstellt";
"lng_group_invite_bad_link" = "Der Einladungslink\nist nicht mehr gültig.";
"lng_group_invite_bad_link" = "Der Einladungslink ist \nnicht mehr gültig.";
"lng_group_invite_want_join" = "Möchtest du der Gruppe «{title}» beitreten?";
"lng_group_invite_join" = "Beitreten";
"lng_group_invite_link" = "Einladungslink";
"lng_group_invite_create" = "Neuer Link";
"lng_group_invite_about" = "Jeder, der Telegram installiert hat, kann \nüber diesen Links in deine Gruppe.";
"lng_group_invite_create_new" = "Neuer Link";
"lng_group_invite_about_new" = "Aktueller Link wird ungültig, wenn \ndu einen neuen erstellst.";
"lng_group_invite_copied" = "Einladungslink in die Zwischenablage kopiert.";
"lng_group_invite_no_room" = "Leider kannst du dieser Gruppe nicht\nmehr beitreten, da sie bereits voll ist.";
"lng_group_invite_about" = "Jeder, der Telegram installiert hat,\nkann anhand dieses Links in deine Gruppe.";
"lng_group_invite_create_new" = "Link widerrufen";
"lng_group_invite_about_new" = "Dein vorheriger Link wird ungültig,\nwenn du einen neuen erstellst.";
"lng_group_invite_copied" = "Der Einladungslink für die Gruppe\nwurde in die Zwischenablage kopiert.";
"lng_group_invite_no_room" = "Leider kannst du dieser Gruppe nicht mehr\nbeitreten, da sie bereits voll ist.";
"lng_forwarded_from" = "Weitergeleitet von";
"lng_in_reply_to" = "Antwort auf";
@@ -420,6 +420,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_media_video" = "Videodatei";
"lng_media_audio" = "Sprachnachricht";
"lng_emoji_category0" = "Häufig genutzt";
"lng_emoji_category1" = "Personen";
"lng_emoji_category2" = "Natur";
"lng_emoji_category3" = "Essen & Trinken";
"lng_emoji_category4" = "Feiern";
"lng_emoji_category5" = "Aktivität";
"lng_emoji_category6" = "Reisen & Orte";
"lng_emoji_category7" = "Objekte & Symbole";
"lng_emoji_category8" = "Sticker";
"lng_in_dlg_photo" = "Bild";
"lng_in_dlg_video" = "Video";
"lng_in_dlg_contact" = "Kontakt";
@@ -563,7 +573,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop wurde aktualisiert auf Version {version}\n\n{changes}\n\nGesamter Versionsverlauf:\n{link}";
"lng_new_version_minor" = "— Fehlerbehebungen und Softwareoptimierungen";
"lng_new_version_text" = "— Einladungslinks für Gruppenchats\n— Graues Kennzeichnungssymbol für stummgeschaltete Chats";
"lng_new_version_text" = "— Neue Emoji hinzugefügt\n— Emoji- und Sticker-Panel wurden verbessert";
"lng_menu_insert_unicode" = "Unicode-Steuerzeichen einfügen";

View File

@@ -389,16 +389,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_changed_title" = "{from} cambió el nombre del grupo a «{title}»";
"lng_action_created_chat" = "{from} creó el grupo «{title}»";
"lng_group_invite_bad_link" = "El enlace de invitación está roto\no ha expirado.";
"lng_group_invite_bad_link" = "El enlace de invitación está \nroto o ha expirado.";
"lng_group_invite_want_join" = "¿Quieres unirte al grupo «{title}»?";
"lng_group_invite_join" = "Unirme";
"lng_group_invite_link" = "Invitación";
"lng_group_invite_create" = "Crear un enlace de invitación";
"lng_group_invite_about" = "Puedes crear un enlace para unirse a este grupo.\nCualquiera que tenga ese enlace puede unirse.";
"lng_group_invite_create_new" = "Nuevo enlace";
"lng_group_invite_about_new" = "El enlace actual dejará de funcionar\ncuando crees uno nuevo.";
"lng_group_invite_copied" = "El enlace de invitación fue copiado al portapapeles.";
"lng_group_invite_about" = "Los usuarios de Telegram podrán unirse\na tu grupo a través de este enlace.";
"lng_group_invite_create_new" = "Anular enlace de invitación";
"lng_group_invite_about_new" = "El enlace previo será desactivado\ny generaremos uno nuevo para ti.";
"lng_group_invite_copied" = "Enlace de invitación copiado al portapapeles.";
"lng_group_invite_no_room" = "No puedes unirte a este grupo porque\nhay muchos miembros en él.";
"lng_forwarded_from" = "Reenviado desde";
@@ -420,6 +420,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_media_video" = "Vídeo";
"lng_media_audio" = "Mensaje de voz";
"lng_emoji_category0" = "Usados frecuentemente";
"lng_emoji_category1" = "Gente";
"lng_emoji_category2" = "Naturaleza";
"lng_emoji_category3" = "Comida y bebidas";
"lng_emoji_category4" = "Celebración";
"lng_emoji_category5" = "Actividad";
"lng_emoji_category6" = "Viajes y lugares";
"lng_emoji_category7" = "Objetos y símbolos";
"lng_emoji_category8" = "Stickers";
"lng_in_dlg_photo" = "Foto";
"lng_in_dlg_video" = "Vídeo";
"lng_in_dlg_contact" = "Contacto";
@@ -563,7 +573,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop fue actualizada a la versión {version}\n\n{changes}\n\nEl historial completo está disponible aquí:\n{link}";
"lng_new_version_minor" = "— Corrección de errores y otras mejoras menores";
"lng_new_version_text" = "— Enlaces de invitación para chats grupales\n— Globo en el ícono gris para conversaciones silenciadas";
"lng_new_version_text" = "— Añadido el soporte para nuevos emojis\n— Pestañas de stickers y emojis mejoradas ";
"lng_menu_insert_unicode" = "Insertar caracteres de control Unicode";

View File

@@ -240,7 +240,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_settings_section_cache" = "Archivio locale";
"lng_settings_no_data_cached" = "Non ci sono dati nella cache!";
"lng_settings_images_cached" = "{count:_not_used_|# immagine|# immagini}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# messaggio vocale|# messaggi vocali}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# nota vocale|# note vocali}, {size}";
"lng_local_storage_clear" = "Elimina tutto";
"lng_local_storage_clearing" = "Eliminando..";
"lng_local_storage_cleared" = "Eliminato!";
@@ -347,7 +347,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_profile_set_group_photo" = "Imposta foto";
"lng_profile_add_participant" = "Aggiungi membro";
"lng_profile_delete_and_exit" = "Esci";
"lng_profile_kick" = "Espelli";
"lng_profile_kick" = "Rimuovi";
"lng_profile_sure_kick" = "Espellere {user} dal gruppo?";
"lng_profile_loading" = "Caricamento..";
"lng_profile_shared_media" = "Media condivisi";
@@ -358,8 +358,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_profile_videos_header" = "Panoramica video";
"lng_profile_files" = "{count:_not_used_|# file|# file} »";
"lng_profile_files_header" = "Panoramica file";
"lng_profile_audios" = "{count:_not_used_|# messaggio vocale|# messaggi vocali} »";
"lng_profile_audios_header" = "Panoramica messaggi vocali";
"lng_profile_audios" = "{count:_not_used_|# nota vocale|# note vocali} »";
"lng_profile_audios_header" = "Panoramica note vocali";
"lng_profile_show_all_types" = "Mostra tutti i tipi";
"lng_profile_copy_phone" = "Copia numero di telefono";
@@ -379,7 +379,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_message_empty" = "(vuoto)";
"lng_action_add_user" = "{from} ha aggiunto {user}";
"lng_action_kick_user" = "{from} ha espulso {user}";
"lng_action_kick_user" = "{from} ha rimosso {user}";
"lng_action_user_left" = "{from} ha lasciato il gruppo";
"lng_action_user_joined" = "{from} si è unito al gruppo";
"lng_action_user_joined_by_link" = "{from} si è unito al gruppo tramite link di invito";
@@ -389,16 +389,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_changed_title" = "{from} ha cambiato il nome del gruppo in «{title}»";
"lng_action_created_chat" = "{from} ha creato il gruppo «{title}»";
"lng_group_invite_bad_link" = "Questo link non funziona\no è scaduto.";
"lng_group_invite_bad_link" = "Questo link di invito non funziona\no è scaduto.";
"lng_group_invite_want_join" = "Vuoi unirti al gruppo «{title}»?";
"lng_group_invite_join" = "Unisciti";
"lng_group_invite_link" = "Link di invito";
"lng_group_invite_create" = "Crea un link di invito";
"lng_group_invite_about" = "Puoi creare un link per unirsi a questo gruppo.\nChiunque abbia quel link potrà unirsi.";
"lng_group_invite_create_new" = "Crea nuovo link";
"lng_group_invite_about_new" = "Il link attuale smetterà di funzionare\nquando ne viene creato uno nuovo.";
"lng_group_invite_copied" = "Il link di invito è stato copiato negli appunti.";
"lng_group_invite_about" = "Gli utenti di Telegram saranno in grado di \naggiungersi al tuo gruppo aprendo il link.";
"lng_group_invite_create_new" = "Revoca link";
"lng_group_invite_about_new" = "Il link precedente smetterà di funzionare\ne ne sarà creato uno nuovo per te.";
"lng_group_invite_copied" = "Link di invito copiato negli appunti.";
"lng_group_invite_no_room" = "Impossibile unirsi a questo gruppo\nperché ci sono già troppi membri.";
"lng_forwarded_from" = "Inoltrato da";
@@ -412,13 +412,23 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_media_type_photos" = "Foto";
"lng_media_type_videos" = "Video";
"lng_media_type_files" = "File";
"lng_media_type_audios" = "Messaggi vocali";
"lng_media_type_audios" = "Note vocali";
"lng_media_open_with" = "Apri con";
"lng_media_download" = "Download";
"lng_media_cancel" = "Annulla";
"lng_media_video" = "Video";
"lng_media_audio" = "Messaggio vocale";
"lng_media_audio" = "Nota vocale";
"lng_emoji_category0" = "Utilizzate di frequente";
"lng_emoji_category1" = "Persone";
"lng_emoji_category2" = "Natura";
"lng_emoji_category3" = "Cibo e bevande";
"lng_emoji_category4" = "Festeggiamenti";
"lng_emoji_category5" = "Attività";
"lng_emoji_category6" = "Viaggi e luoghi";
"lng_emoji_category7" = "Oggetti e simboli";
"lng_emoji_category8" = "Sticker";
"lng_in_dlg_photo" = "Foto";
"lng_in_dlg_video" = "Video";
@@ -563,7 +573,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop si è aggiornato alla versione {version}\n\n{changes}\n\nLa cronologia degli update è disponibile qui:\n{link}";
"lng_new_version_minor" = "— Bug fix e altri miglioramenti minori";
"lng_new_version_text" = "— Link di invito per le chat di gruppo\n— Icona badge grigia per le conversazioni silenziate";
"lng_new_version_text" = "— Aggiunto il supporto per nuove emoji\n— Pannello emoji e sticker migliorato";
"lng_menu_insert_unicode" = "Inserisci carattere di controllo Unicode";

View File

@@ -395,9 +395,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_group_invite_link" = "초대링크";
"lng_group_invite_create" = "초대링크 생성";
"lng_group_invite_about" = "이 그룹방으로 초대할 수 있는 링크를 생성 할 수 있습니다.\n링크가 노출되는 모든 회원이 참여가 가능합니다.";
"lng_group_invite_create_new" = "새로운 링크 생성";
"lng_group_invite_about_new" = "새로 생성하실 경우\n현재 초대링크는 폐기됩니다.";
"lng_group_invite_about" = "이 링크를 통하여,\n그룹방에 초대가 가능합니다.";
"lng_group_invite_create_new" = "초대링크 폐기";
"lng_group_invite_about_new" = "기존 링크는 비활성화 될 예정이며,\n새로운 링크가 재생성이 될 예정입니다.";
"lng_group_invite_copied" = "클립보드에 초대링크가 복사 되었습니다.";
"lng_group_invite_no_room" = "그룹방 인원 최대치가 도달하여\n더이상 참여가 안됩니다.";
@@ -420,6 +420,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_media_video" = "비디오 파일";
"lng_media_audio" = "음성 메시지";
"lng_emoji_category0" = "자주 사용";
"lng_emoji_category1" = "사람";
"lng_emoji_category2" = "자연";
"lng_emoji_category3" = "음식점";
"lng_emoji_category4" = "기념일";
"lng_emoji_category5" = "활동";
"lng_emoji_category6" = "여행 및 장소";
"lng_emoji_category7" = "오브제 & 상징";
"lng_emoji_category8" = "스티커";
"lng_in_dlg_photo" = "사진";
"lng_in_dlg_video" = "비디오";
"lng_in_dlg_contact" = "연락처";
@@ -563,7 +573,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "텔레그램 데스크탑은 {version} 버전으로 업데이트 되었습니다.\n\n{changes}\n\n전체 버전 히스토리는 아래에서 확인 가능합니다:\n{link}";
"lng_new_version_minor" = "— 버그 수정 및 일부 기능 향상";
"lng_new_version_text" = "— 그룹방 초대링크 활성화\n— 음소거된 대화는 회식 뱃지로 표기";
"lng_new_version_text" = "— 새로운 이모티콘 지원\n— 이모티콘 및 스티커 패널 향상";
"lng_menu_insert_unicode" = "유니코드 문자를 입력하세요.";

View File

@@ -395,9 +395,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_group_invite_link" = "Uitnodigingslink";
"lng_group_invite_create" = "Uitnodigingslink maken";
"lng_group_invite_about" = "Andere gebruikers kunnen aan je groep\ndeelnemen door deze link te openen.";
"lng_group_invite_create_new" = "Nieuwe link";
"lng_group_invite_about_new" = "Als je een nieuwe link maakt zal \nde huidige inactief worden.";
"lng_group_invite_about" = "Gebruikers kunnen aan je groep \ndeelnemen met deze link.";
"lng_group_invite_create_new" = "Uitnodigingslink intrekken";
"lng_group_invite_about_new" = "Je uitnodigingslink zal inactief worden\neen nieuwe link zal worden gegenereerd.";
"lng_group_invite_copied" = "Link gekopieerd naar klembord.";
"lng_group_invite_no_room" = "Sorry, deze groep is al vol.";
@@ -420,6 +420,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_media_video" = "Video";
"lng_media_audio" = "Spraakbericht";
"lng_emoji_category0" = "Veelgebruikt";
"lng_emoji_category1" = "Mensen";
"lng_emoji_category2" = "Natuur";
"lng_emoji_category3" = "Eten & drinken";
"lng_emoji_category4" = "Feestelijkheid";
"lng_emoji_category5" = "Actviteit";
"lng_emoji_category6" = "Reizen & plekken";
"lng_emoji_category7" = "Objecten & symbolen";
"lng_emoji_category8" = "Stickers";
"lng_in_dlg_photo" = "Foto";
"lng_in_dlg_video" = "Video";
"lng_in_dlg_contact" = "Contact";
@@ -563,7 +573,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram is bijgewerkt naar versie {version}\n\n{changes} \n\nVolledige versiegeschiedenis is hier te vinden:\n{link}";
"lng_new_version_minor" = "— Probleemoplossing en andere kleine verbeteringen";
"lng_new_version_text" = "— Uitnodigingslinks voor groepschats\n— Grijs Badgenummer voor gesprekken die op stil staan";
"lng_new_version_text" = "— Ondersteuning voor nieuwe emoji toegevoegd\n— Verbeterd emoji- en stickerspaneel";
"lng_menu_insert_unicode" = "Unicode-besturingsteken invoegen";

View File

@@ -389,15 +389,15 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_action_changed_title" = "{from} alterou o título do grupo para «{title}»";
"lng_action_created_chat" = "{from} criou o grupo «{title}»";
"lng_group_invite_bad_link" = "Link de convite quebrado ou expirado.";
"lng_group_invite_bad_link" = "Link de convite quebrado\nou expirado.";
"lng_group_invite_want_join" = "Você deseja entrar no grupo «{title}»?";
"lng_group_invite_join" = "Entrar";
"lng_group_invite_link" = "Link de convite";
"lng_group_invite_create" = "Criar um link de convite";
"lng_group_invite_about" = "Você pode criar um link de convite.\nQuem abrir o link poderá entrar no grupo.";
"lng_group_invite_create_new" = "Criar novo link";
"lng_group_invite_about_new" = "O link de convite atual será desativado\nquando você criar um novo.";
"lng_group_invite_about" = "Usuários do Telegram poderão entrar\nem seu grupo clicando no link.";
"lng_group_invite_create_new" = "Desativar link";
"lng_group_invite_about_new" = "Seu link de convite será desativado\ne iremos gerar um novo link para você.";
"lng_group_invite_copied" = "Link copiado para área de transferência.";
"lng_group_invite_no_room" = "Não foi possível entrar no grupo porque\nele já possui muitos membros.";
@@ -420,6 +420,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_media_video" = "Arquivo de vídeo";
"lng_media_audio" = "Mensagem de voz";
"lng_emoji_category0" = "Frequentemente usado";
"lng_emoji_category1" = "Pessoas";
"lng_emoji_category2" = "Natureza";
"lng_emoji_category3" = "Comidas e Bebidas";
"lng_emoji_category4" = "Celebração";
"lng_emoji_category5" = "Atividade";
"lng_emoji_category6" = "Viagens e Lugares";
"lng_emoji_category7" = "Objetos e Símbolos";
"lng_emoji_category8" = "Stickers";
"lng_in_dlg_photo" = "Foto";
"lng_in_dlg_video" = "Vídeo";
"lng_in_dlg_contact" = "Contato";
@@ -563,7 +573,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop foi atualizado para a versão {version}\n\n{changes}\n\nHistórico completo de mudanças disponível aqui:\n{link}";
"lng_new_version_minor" = "— Resolução de bugs e outras menores melhorias";
"lng_new_version_text" = "— Link de convite para chats em grupo\n— Contador cinza de mensagens não lidas para conversas silenciadas.";
"lng_new_version_text" = "— Adicionado suporte para novos emojis\n— Painel de emojis e stickers melhorado";
"lng_menu_insert_unicode" = "Inserir caractere de controle Unicode";

View File

@@ -188,7 +188,7 @@ void LocalImageLoaderPrivate::prepareImages() {
if (animated) {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (mime == stickerMime && w > 0 && h > 0 && w <= StickerMaxSize && h <= StickerMaxSize && filesize < StickerInMemory) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string("")));
attributes.push_back(MTP_documentAttributeSticker(MTP_string(""), MTP_inputStickerSetEmpty()));
thumbFormat = "webp";
thumbExt = qsl("webp");
}

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;
@@ -1077,10 +1095,17 @@ namespace {
LOG(("App Info: reading old user config.."));
qint32 version = 0;
mtpDcOptions dcOpts(cDcOptions());
mtpDcOptions dcOpts;
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = cDcOptions();
}
_dcOpts = &dcOpts;
_readOldUserSettingsFields(&file, version);
cSetDcOptions(dcOpts);
{
QWriteLocker lock(MTP::dcOptionsMutex());
cSetDcOptions(dcOpts);
}
file.close();
result = true;
@@ -1157,10 +1182,17 @@ namespace {
LOG(("App Info: reading old keys.."));
qint32 version = 0;
mtpDcOptions dcOpts(cDcOptions());
mtpDcOptions dcOpts;
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = cDcOptions();
}
_dcOpts = &dcOpts;
_readOldMtpDataFields(&file, version);
cSetDcOptions(dcOpts);
{
QWriteLocker lock(MTP::dcOptionsMutex());
cSetDcOptions(dcOpts);
}
file.close();
result = true;
@@ -1178,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);
@@ -1197,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);
@@ -1326,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;
@@ -1366,7 +1412,7 @@ namespace {
storageImagesSize += size;
}
} break;
case lskStickers: {
case lskStickerImages: {
quint32 count = 0;
map.stream >> count;
for (quint32 i = 0; i < count; ++i) {
@@ -1374,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;
@@ -1393,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;
@@ -1405,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;
@@ -1420,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;
@@ -1486,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);
@@ -1512,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);
}
}
@@ -1527,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);
@@ -1648,7 +1702,11 @@ namespace Local {
LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode.."));
return writeSettings();
}
mtpDcOptions dcOpts(cDcOptions());
mtpDcOptions dcOpts;
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = cDcOptions();
}
_dcOpts = &dcOpts;
LOG(("App Info: reading encrypted settings.."));
while (!settings.stream.atEnd()) {
@@ -1669,7 +1727,10 @@ namespace Local {
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
}
}
cSetDcOptions(dcOpts);
{
QWriteLocker lock(MTP::dcOptionsMutex());
cSetDcOptions(dcOpts);
}
_settingsSalt = salt;
}
@@ -1690,13 +1751,19 @@ namespace Local {
}
settings.writeData(_settingsSalt);
mtpDcOptions dcOpts(cDcOptions());
mtpDcOptions dcOpts;
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = cDcOptions();
}
if (dcOpts.isEmpty()) {
const BuiltInDc *bdcs = builtInDcs();
for (int i = 0, l = builtInDcsCount(); i < l; ++i) {
dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port));
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
}
QWriteLocker lock(MTP::dcOptionsMutex());
cSetDcOptions(dcOpts);
}
@@ -1759,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);
@@ -1969,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();
@@ -2025,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() {
@@ -2036,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();
@@ -2056,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();
}
@@ -2081,7 +2148,7 @@ namespace Local {
}
int32 hasStickers() {
return _stickersMap.size();
return _stickerImagesMap.size();
}
qint64 storageStickersSize() {
@@ -2140,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->alt);
// 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->alt;
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;
@@ -2199,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;
@@ -2207,16 +2309,115 @@ namespace Local {
if (type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (type == StickerDocument) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt)));
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
}
if (width > 0 && height > 0) {
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) {
@@ -2385,8 +2586,8 @@ namespace Local {
_storageImagesSize = 0;
_mapChanged = true;
}
if (!_stickersMap.isEmpty()) {
_stickersMap.clear();
if (!_stickerImagesMap.isEmpty()) {
_stickerImagesMap.clear();
_storageStickersSize = 0;
_mapChanged = true;
}
@@ -2407,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) {
@@ -2435,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;
@@ -2445,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

@@ -26,6 +26,8 @@ int main(int argc, char *argv[]) {
_oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter);
#endif
InitOpenSSL _init;
settingsParseArgs(argc, argv);
for (int32 i = 0; i < argc; ++i) {
if (string("-fixprevious") == argv[i]) {
@@ -61,7 +63,12 @@ int main(int argc, char *argv[]) {
psStart();
int result = 0;
{
Application app(argc, argv);
QByteArray args[] = { "-style=0" }; // prepare fake args
static const int a_cnt = sizeof(args) / sizeof(args[0]);
int a_argc = a_cnt + 1;
char *a_argv[a_cnt + 1] = { argv[0], args[0].data() };
Application app(a_argc, a_argv);
if (!App::quiting()) {
result = app.exec();
}

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

@@ -775,9 +775,9 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
_doc = doc;
QString already = _doc->already(true);
if (!_doc->sticker->isNull() && _doc->sticker->loaded()) {
if (_doc->sticker && !_doc->sticker->img->isNull() && _doc->sticker->img->loaded()) {
_currentGif.stop();
_current = _doc->sticker->pix();
_current = _doc->sticker->img->pix();
} else if (!already.isEmpty()) {
QImageReader reader(already);
if (reader.canRead()) {
@@ -980,7 +980,7 @@ void MediaView::paintEvent(QPaintEvent *e) {
QRect imgRect(_x, _y, _w, _h);
const QPixmap *toDraw = _currentGif.isNull() ? &_current : &_currentGif.frames[_currentGif.frame];
if (imgRect.intersects(r)) {
if (toDraw->hasAlpha() && (!_doc || _doc->sticker->isNull())) {
if (toDraw->hasAlpha() && (!_doc || !_doc->sticker || _doc->sticker->img->isNull())) {
p.fillRect(imgRect, _transparentBrush);
}
if (_zoom) {
@@ -1368,7 +1368,7 @@ void MediaView::preloadData(int32 delta) {
switch (media->type()) {
case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->full->load(); break;
case MediaTypeDocument: static_cast<HistoryDocument*>(media)->document()->thumb->load(); break;
case MediaTypeSticker: static_cast<HistorySticker*>(media)->document()->sticker->load(); break;
case MediaTypeSticker: static_cast<HistorySticker*>(media)->document()->sticker->img->load(); break;
}
}
}

View File

@@ -117,6 +117,7 @@ with open('scheme.tl') as f:
prms = {};
conditions = {};
prmsList = [];
conditionsList = [];
isTemplate = hasFlags = hasTemplate = '';
for param in paramsList:
if (re.match(r'^\s*$', param)):
@@ -149,7 +150,9 @@ with open('scheme.tl') as f:
print('Bad param found: "' + param + '" in line: ' + line);
continue;
ptype = pmasktype.group(3);
conditions[pname] = pmasktype.group(2);
if (not pname in conditions):
conditionsList.append(pname);
conditions[pname] = pmasktype.group(2);
elif (ptype.find('<') >= 0):
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._]+)>$', ptype);
if (templ):
@@ -198,11 +201,11 @@ with open('scheme.tl') as f:
if (len(conditions)):
funcsText += '\n';
funcsText += '\tenum {\n';
for paramName in conditions.keys():
for paramName in conditionsList:
funcsText += '\t\tflag_' + paramName + ' = (1 << ' + conditions[paramName] + '),\n';
funcsText += '\t};\n';
funcsText += '\n';
for paramName in conditions.keys():
for paramName in conditionsList:
funcsText += '\tbool has_' + paramName + '() const { return v' + hasFlags + '.v & flag_' + paramName + '; }\n';
funcsText += '\n';
@@ -210,7 +213,7 @@ with open('scheme.tl') as f:
size = [];
for k in prmsList:
v = prms[k];
if (k in conditions.keys()):
if (k in conditionsList):
size.append('(has_' + k + '() ? v' + k + '.innerLength() : 0)');
else:
size.append('v' + k + '.innerLength()');
@@ -224,7 +227,7 @@ with open('scheme.tl') as f:
funcsText += '\tvoid read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_' + name + ') {\n'; # read method
for k in prmsList:
v = prms[k];
if (k in conditions.keys()):
if (k in conditionsList):
funcsText += '\t\tif (has_' + k + '()) { v' + k + '.read(from, end); } else { v' + k + ' = MTP' + v + '(); }\n';
else:
funcsText += '\t\tv' + k + '.read(from, end);\n';
@@ -233,7 +236,7 @@ with open('scheme.tl') as f:
funcsText += '\tvoid write(mtpBuffer &to) const {\n'; # write method
for k in prmsList:
v = prms[k];
if (k in conditions.keys()):
if (k in conditionsList):
funcsText += '\t\tif (has_' + k + '()) v' + k + '.write(to);\n';
else:
funcsText += '\t\tv' + k + '.write(to);\n';
@@ -269,7 +272,7 @@ with open('scheme.tl') as f:
funcsList.append(restype);
funcsDict[restype] = [];
# TypesDict[restype] = resType;
funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditions]);
funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions]);
else:
if (isTemplate != ''):
print('Template types not allowed: "' + resType + '" in line: ' + line);
@@ -278,7 +281,7 @@ with open('scheme.tl') as f:
typesList.append(restype);
typesDict[restype] = [];
TypesDict[restype] = resType;
typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditions]);
typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, conditionsList, conditions]);
consts = consts + 1;
@@ -292,7 +295,8 @@ def addTextSerialize(lst, dct, dataLetter):
prmsList = data[2];
prms = data[3];
hasFlags = data[4];
conditions = data[5];
conditionsList = data[5];
conditions = data[6];
if len(result):
result += '\n';
@@ -311,7 +315,7 @@ def addTextSerialize(lst, dct, dataLetter):
result += '\t\t\t\tcase ' + str(stage) + ': to.add(" ' + k + ': "); ++stages.back(); ';
if (k == hasFlags):
result += 'if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; ';
if (k in conditions.keys()):
if (k in conditionsList):
result += 'if (flag & MTP' + dataLetter + name + '::flag_' + k + ') { ';
result += 'types.push_back(';
vtypeget = re.match(r'^[Vv]ector<MTP([A-Za-z0-9\._]+)>', v);
@@ -355,7 +359,7 @@ def addTextSerialize(lst, dct, dataLetter):
else:
result += '0); vtypes.push_back(0';
result += '); stages.push_back(0); flags.push_back(0); ';
if (k in conditions.keys()):
if (k in conditionsList):
result += '} else { to.add("[ SKIPPED BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); } ';
result += 'break;\n';
stage = stage + 1;
@@ -396,7 +400,8 @@ for restype in typesList:
prmsList = data[2];
prms = data[3];
hasFlags = data[4];
conditions = data[5];
conditionsList = data[5];
conditions = data[6];
dataText = '';
dataText += '\nclass MTPD' + name + ' : public mtpDataImpl<MTPD' + name + '> {\n'; # data class
@@ -451,7 +456,7 @@ for restype in typesList:
if (withType):
readText += '\t\t';
writeText += '\t\t';
if (paramName in conditions.keys()):
if (paramName in conditionsList):
readText += '\tif (v.has_' + paramName + '()) { v.v' + paramName + '.read(from, end); } else { v.v' + paramName + ' = MTP' + paramType + '(); }\n';
writeText += '\tif (v.has_' + paramName + '()) v.v' + paramName + '.write(to);\n';
sizeList.append('(v.has_' + paramName + '() ? v.v' + paramName + '.innerLength() : 0)');
@@ -482,11 +487,11 @@ for restype in typesList:
if (len(conditions)):
dataText += '\n';
dataText += '\tenum {\n';
for paramName in conditions.keys():
for paramName in conditionsList:
dataText += '\t\tflag_' + paramName + ' = (1 << ' + conditions[paramName] + '),\n';
dataText += '\t};\n';
dataText += '\n';
for paramName in conditions.keys():
for paramName in conditionsList:
dataText += '\tbool has_' + paramName + '() const { return v' + hasFlags + '.v & flag_' + paramName + '; }\n';
dataText += '};\n'; # class ending

View File

@@ -23,13 +23,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
namespace {
typedef QMap<int32, MTProtoSessionPtr> Sessions;
Sessions sessions;
QVector<MTProtoSessionPtr> sessionsToKill;
MTProtoSessionPtr mainSession;
typedef QMap<mtpRequestId, int32> RequestsByDC; // holds dc for request to this dc or -dc for request to main dc
typedef QMap<mtpRequestId, int32> RequestsByDC; // holds dcWithShift for request to this dc or -dc for request to main dc
RequestsByDC requestsByDC;
QMutex requestByDCLock;
typedef QMap<mtpRequestId, int32> AuthExportRequests; // holds target dc for auth export request
typedef QMap<mtpRequestId, int32> AuthExportRequests; // holds target dcWithShift for auth export request
AuthExportRequests authExportRequests;
bool _started = false;
@@ -55,7 +56,7 @@ namespace {
BadGuestDCRequests badGuestDCRequests;
typedef QVector<mtpRequestId> DCAuthWaiters;
typedef QMap<int32, DCAuthWaiters> AuthWaiters;
typedef QMap<int32, DCAuthWaiters> AuthWaiters; // holds request ids waiting for auth import to specific dc
AuthWaiters authWaiters;
QMutex toClearLock;
@@ -76,12 +77,11 @@ namespace {
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
return;
}
int32 newdc = i.value();
int32 newdc = i.value() % _mtp_internal::dcShift;
DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc));
DCAuthWaiters &waiters(authWaiters[newdc]);
MTProtoSessionPtr session(_mtp_internal::getSession(newdc));
if (waiters.size()) {
QReadLocker locker(&requestMapLock);
for (DCAuthWaiters::iterator i = waiters.begin(), e = waiters.end(); i != e; ++i) {
@@ -91,6 +91,7 @@ namespace {
LOG(("MTP Error: could not find request %1 for resending").arg(requestId));
continue;
}
int32 dcWithShift = newdc;
{
RequestsByDC::iterator k = requestsByDC.find(requestId);
if (k == requestsByDC.cend()) {
@@ -101,11 +102,15 @@ namespace {
MTP::setdc(newdc);
k.value() = -newdc;
} else {
k.value() = k.value() - (k.value() % _mtp_internal::dcShift) + newdc;
int32 shift = k.value() - (k.value() % _mtp_internal::dcShift);
dcWithShift += shift;
k.value() = dcWithShift;
}
DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(requestId).arg(k.value()));
}
session->sendPrepared(j.value());
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift)) {
session->sendPrepared(j.value());
}
}
waiters.clear();
}
@@ -121,8 +126,8 @@ namespace {
void exportDone(const MTPauth_ExportedAuthorization &result, mtpRequestId req) {
AuthExportRequests::const_iterator i = authExportRequests.constFind(req);
if (i == authExportRequests.cend()) {
LOG(("MTP Error: auth export request target dc not found, requestId: %1").arg(req));
RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dc, request %1").arg(req)));
LOG(("MTP Error: auth export request target dcWithShift not found, requestId: %1").arg(req));
RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dcWithShift, request %1").arg(req)));
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
return;
}
@@ -137,7 +142,7 @@ namespace {
AuthExportRequests::const_iterator i = authExportRequests.constFind(req);
if (i != authExportRequests.cend()) {
authWaiters[i.value()].clear();
authWaiters[i.value() % _mtp_internal::dcShift].clear();
}
if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc
return true;
@@ -151,31 +156,34 @@ namespace {
if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) {
if (!requestId) return false;
int32 dc = 0, newdc = m.captured(2).toInt();
int32 dcWithShift = 0, newdcWithShift = m.captured(2).toInt();
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i == requestsByDC.end()) {
LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdc));
LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdcWithShift));
} else {
dc = i.value();
dcWithShift = i.value();
}
}
if (!dc || !newdc) return false;
if (!dcWithShift || !newdcWithShift) return false;
DEBUG_LOG(("MTP Info: changing request %1 dc%2 to %3").arg(requestId).arg((dc > 0) ? "" : " and main dc").arg(newdc));
if (dc < 0) {
if (MTP::authedId() && !authExportRequests.contains(requestId)) { // import auth, set dc and resend
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdc));
DCAuthWaiters &waiters(authWaiters[newdc]);
DEBUG_LOG(("MTP Info: changing request %1 from dcWithShift%2 to dc%3").arg(requestId).arg(dcWithShift).arg(newdcWithShift));
if (dcWithShift < 0) { // newdc shift = 0
if (false && MTP::authedId() && !authExportRequests.contains(requestId)) { // migrate not supported at this moment
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdcWithShift));
DCAuthWaiters &waiters(authWaiters[newdcWithShift]);
if (!waiters.size()) {
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc);
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdcWithShift)), rpcDone(exportDone), rpcFail(exportFail)), newdcWithShift);
}
waiters.push_back(requestId);
return true;
} else {
MTP::setdc(newdc);
MTP::setdc(newdcWithShift);
}
} else {
int32 shift = dcWithShift - (dcWithShift % _mtp_internal::dcShift);
newdcWithShift += shift;
}
mtpRequest req;
@@ -188,8 +196,10 @@ namespace {
}
req = i.value();
}
_mtp_internal::registerRequest(requestId, (dc < 0) ? -newdc : newdc);
_mtp_internal::getSession(newdc)->sendPrepared(req);
if (MTProtoSessionPtr session = _mtp_internal::getSession(newdcWithShift)) {
_mtp_internal::registerRequest(requestId, (dcWithShift < 0) ? -newdcWithShift : newdcWithShift);
session->sendPrepared(req);
}
return true;
} else if (code < 0 || code >= 500 || (m = QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err)).hasMatch()) {
if (!requestId) return false;
@@ -218,26 +228,26 @@ namespace {
return true;
} else if (code == 401 || (badGuestDC && badGuestDCRequests.constFind(requestId) == badGuestDCRequests.cend())) {
int32 dc = 0;
int32 dcWithShift = 0;
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
dc = i.value();
dcWithShift = i.value();
} else {
LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
}
}
int32 newdc = abs(dc) % _mtp_internal::dcShift;
int32 newdc = abs(dcWithShift) % _mtp_internal::dcShift;
if (!newdc || newdc == mtpMainDC() || !MTP::authedId()) {
if (!badGuestDC && globalHandler.onFail) (*globalHandler.onFail)(requestId, error); // auth failed in main dc
return false;
}
DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(dc));
DEBUG_LOG(("MTP Info: importing auth to dcWithShift %1").arg(dcWithShift));
DCAuthWaiters &waiters(authWaiters[newdc]);
if (!waiters.size()) {
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc);
authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), abs(dcWithShift));
}
waiters.push_back(requestId);
if (badGuestDC) badGuestDCRequests.insert(requestId);
@@ -253,20 +263,22 @@ namespace {
}
req = i.value();
}
int32 dc = 0;
int32 dcWithShift = 0;
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i == requestsByDC.end()) {
LOG(("MTP Error: could not find request %1 for resending with init connection").arg(requestId));
} else {
dc = i.value();
dcWithShift = i.value();
}
}
if (!dc) return false;
if (!dcWithShift) return false;
req->needsLayer = true;
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req);
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
req->needsLayer = true;
session->sendPrepared(req);
}
return true;
} else if (err == qsl("MSG_WAIT_FAILED")) {
mtpRequest req;
@@ -283,7 +295,7 @@ namespace {
LOG(("MTP Error: wait failed for not dependent request %1").arg(requestId));
return false;
}
int32 dc = 0;
int32 dcWithShift = 0;
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId), j = requestsByDC.find(req->after->requestId);
@@ -292,19 +304,21 @@ namespace {
} else if (j == requestsByDC.end()) {
LOG(("MTP Error: could not find dependent request %1 by dc").arg(req->after->requestId));
} else {
dc = i.value();
dcWithShift = i.value();
if (i.value() != j.value()) {
req->after = mtpRequest();
}
}
}
if (!dc) return false;
if (!dcWithShift) return false;
if (!req->after) {
req->needsLayer = true;
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req);
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
req->needsLayer = true;
session->sendPrepared(req);
}
} else {
int32 newdc = abs(dc) % _mtp_internal::dcShift;
int32 newdc = abs(dcWithShift) % _mtp_internal::dcShift;
DCAuthWaiters &waiters(authWaiters[newdc]);
if (waiters.indexOf(req->after->requestId) >= 0) {
if (waiters.indexOf(requestId) < 0) {
@@ -338,27 +352,27 @@ namespace {
}
namespace _mtp_internal {
MTProtoSessionPtr getSession(int32 dc) {
MTProtoSessionPtr getSession(int32 dcWithShift) {
if (!_started) return MTProtoSessionPtr();
if (!dc) return mainSession;
if (!(dc % _mtp_internal::dcShift)) {
dc += mainSession->getDC();
if (!dcWithShift) return mainSession;
if (!(dcWithShift % _mtp_internal::dcShift)) {
dcWithShift += (mainSession->getDcWithShift() % _mtp_internal::dcShift);
}
Sessions::const_iterator i = sessions.constFind(dc);
Sessions::const_iterator i = sessions.constFind(dcWithShift);
if (i != sessions.cend()) return *i;
MTProtoSessionPtr result(new MTProtoSession());
result->start(dc);
result->start(dcWithShift);
sessions.insert(dc, result);
sessions.insert(dcWithShift, result);
return result;
}
void registerRequest(mtpRequestId requestId, int32 dc) {
void registerRequest(mtpRequestId requestId, int32 dcWithShift) {
{
QMutexLocker locker(&requestByDCLock);
requestsByDC.insert(requestId, dc);
requestsByDC.insert(requestId, dcWithShift);
}
_mtp_internal::performDelayedClear(); // need to do it somewhere..
}
@@ -530,12 +544,12 @@ namespace _mtp_internal {
if (globalHandler.onDone) (*globalHandler.onDone)(0, from, end); // some updates were received
}
void onStateChange(int32 dc, int32 state) {
if (stateChangedHandler) stateChangedHandler(dc, state);
void onStateChange(int32 dcWithShift, int32 state) {
if (stateChangedHandler) stateChangedHandler(dcWithShift, state);
}
void onSessionReset(int32 dc) {
if (sessionResetHandler) sessionResetHandler(dc);
void onSessionReset(int32 dcWithShift) {
if (sessionResetHandler) sessionResetHandler(dcWithShift);
}
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err) { // return true if need to clean request data
@@ -561,12 +575,12 @@ namespace _mtp_internal {
mtpRequestId requestId = delayedRequests.front().first;
delayedRequests.pop_front();
int32 dc = 0;
int32 dcWithShift = 0;
{
QMutexLocker locker(&requestByDCLock);
RequestsByDC::const_iterator i = requestsByDC.constFind(requestId);
if (i != requestsByDC.cend()) {
dc = i.value();
dcWithShift = i.value();
} else {
LOG(("MTP Error: could not find request dc for delayed resend, requestId %1").arg(requestId));
continue;
@@ -583,7 +597,9 @@ namespace _mtp_internal {
}
req = j.value();
}
_mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req);
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
session->sendPrepared(req);
}
}
if (!delayedRequests.isEmpty()) {
@@ -595,13 +611,15 @@ namespace _mtp_internal {
namespace MTP {
void start() {
if (started()) return;
unixtimeInit();
MTProtoDCMap &dcs(mtpDCMap());
mainSession = MTProtoSessionPtr(new MTProtoSession());
mainSession->start(mtpMainDC());
sessions[mainSession->getDC()] = mainSession;
sessions[mainSession->getDcWithShift()] = mainSession;
_started = true;
resender = new _mtp_internal::RequestResender();
@@ -625,8 +643,9 @@ namespace MTP {
void restart(int32 dcMask) {
if (!_started) return;
dcMask %= _mtp_internal::dcShift;
for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) {
if ((*i)->getDC() % _mtp_internal::dcShift == dcMask % _mtp_internal::dcShift) {
if (((*i)->getDcWithShift() % _mtp_internal::dcShift) == dcMask) {
(*i)->restart();
}
}
@@ -641,8 +660,9 @@ namespace MTP {
void setdc(int32 dc, bool fromZeroOnly) {
if (!dc || !_started) return;
mtpSetDC(dc, fromZeroOnly);
if (maindc() != mainSession->getDC()) {
mainSession = _mtp_internal::getSession(maindc());
int32 oldMainDc = mainSession->getDcWithShift();
if (maindc() != oldMainDc) {
killSession(oldMainDc);
}
Local::writeMtpData();
}
@@ -656,7 +676,7 @@ namespace MTP {
if (!dc) return mainSession->getState();
if (!(dc % _mtp_internal::dcShift)) {
dc += mainSession->getDC();
dc += (mainSession->getDcWithShift() % _mtp_internal::dcShift);
}
Sessions::const_iterator i = sessions.constFind(dc);
@@ -670,7 +690,7 @@ namespace MTP {
if (!dc) return mainSession->transport();
if (!(dc % _mtp_internal::dcShift)) {
dc += mainSession->getDC();
dc += (mainSession->getDcWithShift() % _mtp_internal::dcShift);
}
Sessions::const_iterator i = sessions.constFind(dc);
@@ -679,16 +699,10 @@ namespace MTP {
return QString();
}
void initdc(int32 dc) {
if (!_started) return;
_mtp_internal::getSession(dc);
}
void ping() {
MTProtoSessionPtr session = _mtp_internal::getSession(0);
if (!session) return;
return session->ping();
if (MTProtoSessionPtr session = _mtp_internal::getSession(0)) {
session->ping();
}
}
void cancel(mtpRequestId requestId) {
@@ -706,25 +720,35 @@ namespace MTP {
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
_mtp_internal::getSession(abs(i.value()))->cancel(requestId, msgId);
if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) {
session->cancel(requestId, msgId);
}
requestsByDC.erase(i);
}
}
_mtp_internal::clearCallbacks(requestId);
}
void killSessionsDelayed() {
if (!sessionsToKill.isEmpty()) {
sessionsToKill.clear();
}
}
void killSession(int32 dc) {
Sessions::iterator i = sessions.find(dc);
if (i != sessions.end()) {
bool wasMain = (i.value() == mainSession);
i.value()->stop();
i.value()->kill();
if (sessionsToKill.isEmpty()) QTimer::singleShot(0, killSessionsDelayed);
sessionsToKill.push_back(i.value());
sessions.erase(i);
if (wasMain) {
mainSession = MTProtoSessionPtr(new MTProtoSession());
mainSession->start(mtpMainDC());
sessions[mainSession->getDC()] = mainSession;
sessions[mainSession->getDcWithShift()] = mainSession;
}
}
}
@@ -743,22 +767,30 @@ namespace MTP {
QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) {
return _mtp_internal::getSession(abs(i.value()))->requestState(requestId);
if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) {
return session->requestState(requestId);
}
return MTP::RequestConnecting;
}
return MTP::RequestSent;
}
return _mtp_internal::getSession(-requestId)->requestState(0);
if (MTProtoSessionPtr session = _mtp_internal::getSession(-requestId)) {
return session->requestState(0);
}
return MTP::RequestConnecting;
}
void stop() {
for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) {
i.value()->stop();
i.value()->kill();
}
sessions.clear();
mainSession = MTProtoSessionPtr();
delete resender;
resender = 0;
mtpDestroyConfigLoader();
_started = false;
}
void authed(int32 uid) {
@@ -810,4 +842,8 @@ namespace MTP {
return mtpSetKey(dc, key);
}
QReadWriteLock *dcOptionsMutex() {
return mtpDcOptionsMutex();
}
};

View File

@@ -37,8 +37,8 @@ namespace _mtp_internal {
void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end);
bool hasCallbacks(mtpRequestId requestId);
void globalCallback(const mtpPrime *from, const mtpPrime *end);
void onStateChange(int32 dc, int32 state);
void onSessionReset(int32 dc);
void onStateChange(int32 dcWithShift, int32 state);
void onSessionReset(int32 dcWithShift);
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); // return true if need to clean request data
inline bool rpcErrorOccured(mtpRequestId requestId, const RPCResponseHandler &handler, const RPCError &err) {
return rpcErrorOccured(requestId, handler.onFail, err);
@@ -64,6 +64,7 @@ namespace _mtp_internal {
namespace MTP {
static const uint32 cfg = 1 * _mtp_internal::dcShift; // send(MTPhelp_GetConfig(), MTP::cfg + dc) - for dc enum
static const uint32 lgt = 2 * _mtp_internal::dcShift; // send(MTPauth_LogOut(), MTP::lgt + dc) - for logout of guest dcs enum
static const uint32 dld[MTPDownloadSessionsCount] = { // send(req, callbacks, MTP::dld[i] + dc) - for download
0x10 * _mtp_internal::dcShift,
0x11 * _mtp_internal::dcShift,
@@ -89,13 +90,13 @@ namespace MTP {
int32 dcstate(int32 dc = 0);
QString dctransport(int32 dc = 0);
void initdc(int32 dc);
template <typename TRequest>
inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
MTProtoSessionPtr session = _mtp_internal::getSession(dc);
if (!session) return 0;
return session->send(request, callbacks, msCanWait, true, !dc, after);
if (MTProtoSessionPtr session = _mtp_internal::getSession(dc)) {
return session->send(request, callbacks, msCanWait, true, !dc, after);
}
return 0;
}
template <typename TRequest>
inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
@@ -139,6 +140,8 @@ namespace MTP {
mtpKeysMap getKeys();
void setKey(int32 dc, mtpAuthKeyPtr key);
QReadWriteLock *dcOptionsMutex();
};
#include "mtproto/mtpSessionImpl.h"

View File

@@ -1114,6 +1114,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
// createConn();
if (!dc) {
QReadLocker lock(mtpDcOptionsMutex());
const mtpDcOptions &options(cDcOptions());
if (options.isEmpty()) {
LOG(("MTP Error: connect failed, no DCs"));
@@ -1713,6 +1714,8 @@ void MTProtoConnectionPrivate::retryByTimer() {
}
if (keyId == mtpAuthKey::RecreateKeyId) {
if (sessionData->getKey()) {
unlockKey();
QWriteLocker lock(sessionData->keyMutex());
sessionData->owner()->destroyKey();
}
@@ -1738,31 +1741,34 @@ void MTProtoConnectionPrivate::socketStart(bool afterConfig) {
_pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0;
_pingSender.stop();
const mtpDcOption *dcOption = 0;
const mtpDcOptions &options(cDcOptions());
mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift);
DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc));
if (dcIndex == options.cend()) {
std::string ip;
uint32 port = 0;
{
QReadLocker lock(mtpDcOptionsMutex());
const mtpDcOptions &options(cDcOptions());
mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift);
DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc));
if (dcIndex != options.cend()) {
ip = dcIndex->ip;
port = dcIndex->port;
}
}
if (!port || ip.empty()) {
if (afterConfig) {
LOG(("MTP Error: DC %1 options not found right after config load!").arg(dc));
return restart();
} else {
DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(dc));
connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded()));
mtpConfigLoader()->load();
return;
}
DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(dc));
connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded()));
mtpConfigLoader()->load();
return;
}
dcOption = &dcIndex.value();
const char *ip(dcOption->ip.c_str());
uint32 port(dcOption->port);
DEBUG_LOG(("MTP Info: socket connection to %1:%2..").arg(ip).arg(port));
DEBUG_LOG(("MTP Info: socket connection to %1:%2..").arg(ip.c_str()).arg(port));
connect(conn, SIGNAL(connected()), this, SLOT(onConnected()));
connect(conn, SIGNAL(disconnected()), this, SLOT(restart()));
conn->connectToServer(ip, port);
conn->connectToServer(ip.c_str(), port);
}
void MTProtoConnectionPrivate::restart(bool maybeBadKey) {
@@ -2824,12 +2830,36 @@ void MTProtoConnectionPrivate::onConnected() {
TCP_LOG(("Connection Info: connection succeed."));
if (updateAuthKey()) {
DEBUG_LOG(("MTP Info: returning from socketConnected.."));
return;
updateAuthKey();
}
void MTProtoConnectionPrivate::updateAuthKey() {
QReadLocker lockFinished(&sessionDataMutex);
if (!sessionData || !conn) return;
DEBUG_LOG(("AuthKey Info: MTProtoConnection updating key from MTProtoSession, dc %1").arg(dc));
uint64 newKeyId = 0;
{
ReadLockerAttempt lock(sessionData->keyMutex());
if (!lock) {
DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit"));
clearMessages();
keyId = newKeyId;
return; // some other connection is getting key
}
const mtpAuthKeyPtr &key(sessionData->getKey());
newKeyId = key ? key->keyId() : 0;
}
if (keyId != newKeyId) {
clearMessages();
keyId = newKeyId;
}
DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(mb(&keyId, sizeof(keyId)).str()));
if (keyId) {
return authKeyCreated();
}
DEBUG_LOG(("MTP Info: will be creating auth_key"));
DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), will be creating auth_key"));
lockKey();
const mtpAuthKeyPtr &key(sessionData->getKey());
@@ -2854,36 +2884,6 @@ void MTProtoConnectionPrivate::onConnected() {
sendRequestNotSecure(req_pq);
}
bool MTProtoConnectionPrivate::updateAuthKey() {
QReadLocker lockFinished(&sessionDataMutex);
if (!sessionData || !conn) return false;
DEBUG_LOG(("AuthKey Info: MTProtoConnection updating key from MTProtoSession, dc %1").arg(dc));
uint64 newKeyId = 0;
{
ReadLockerAttempt lock(sessionData->keyMutex());
if (!lock) {
DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit"));
clearMessages();
keyId = newKeyId;
return true; // some other connection is getting key
}
const mtpAuthKeyPtr &key(sessionData->getKey());
newKeyId = key ? key->keyId() : 0;
}
if (keyId != newKeyId) {
clearMessages();
keyId = newKeyId;
}
DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(mb(&keyId, sizeof(keyId)).str()));
if (keyId) {
authKeyCreated();
return true;
}
DEBUG_LOG(("AuthKey Info: Key update failed"));
return false;
}
void MTProtoConnectionPrivate::clearMessages() {
if (keyId && keyId != mtpAuthKey::RecreateKeyId && conn) {
conn->received().clear();
@@ -3481,7 +3481,14 @@ MTProtoConnectionPrivate::~MTProtoConnectionPrivate() {
void MTProtoConnectionPrivate::stop() {
QWriteLocker lockFinished(&sessionDataMutex);
sessionData = 0;
if (sessionData) {
if (myKeyLock) {
sessionData->owner()->notifyKeyCreated(mtpAuthKeyPtr()); // release key lock, let someone else create it
sessionData->keyMutex()->unlock();
myKeyLock = false;
}
sessionData = 0;
}
}
MTProtoConnection::~MTProtoConnection() {

View File

@@ -366,7 +366,7 @@ public slots:
// Sessions signals, when we need to send something
void tryToSend();
bool updateAuthKey();
void updateAuthKey();
void onConfigLoaded();

View File

@@ -366,7 +366,7 @@ static const mtpTypeId mtpLayers[] = {
mtpc_invokeWithLayer17,
mtpc_invokeWithLayer18,
}, mtpLayerMaxSingle = sizeof(mtpLayers) / sizeof(mtpLayers[0]);
static const mtpPrime mtpCurrentLayer = 28;
static const mtpPrime mtpCurrentLayer = 29;
template <typename bareT>
class MTPBoxed : public bareT {

View File

@@ -56,6 +56,20 @@ int32 mtpMainDC() {
return mainDC;
}
namespace {
QMap<int32, mtpRequestId> logoutGuestMap; // dcWithShift to logout request id
bool logoutDone(mtpRequestId req) {
for (QMap<int32, mtpRequestId>::iterator i = logoutGuestMap.begin(); i != logoutGuestMap.end(); ++i) {
if (i.value() == req) {
MTP::killSession(i.key());
logoutGuestMap.erase(i);
return true;
}
}
return false;
}
}
void mtpLogoutOtherDCs() {
QList<int32> dcs;
{
@@ -64,7 +78,7 @@ void mtpLogoutOtherDCs() {
}
for (int32 i = 0, cnt = dcs.size(); i != cnt; ++i) {
if (dcs[i] != MTP::maindc()) {
MTP::send(MTPauth_LogOut(), RPCResponseHandler(), dcs[i]);
logoutGuestMap.insert(MTP::lgt + dcs[i], MTP::send(MTPauth_LogOut(), rpcDone(&logoutDone), rpcFail(&logoutDone), MTP::lgt + dcs[i]));
}
}
}
@@ -154,7 +168,11 @@ namespace {
void mtpUpdateDcOptions(const QVector<MTPDcOption> &options) {
QSet<int32> already, restart;
{
mtpDcOptions opts(cDcOptions());
mtpDcOptions opts;
{
QReadLocker lock(mtpDcOptionsMutex());
opts = cDcOptions();
}
for (QVector<MTPDcOption>::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) {
const MTPDdcOption &optData(i->c_dcOption());
if (already.constFind(optData.vid.v) == already.cend()) {
@@ -168,16 +186,26 @@ void mtpUpdateDcOptions(const QVector<MTPDcOption> &options) {
opts.insert(optData.vid.v, mtpDcOption(optData.vid.v, optData.vhostname.c_string().v, optData.vip_address.c_string().v, optData.vport.v));
}
}
cSetDcOptions(opts);
{
QWriteLocker lock(mtpDcOptionsMutex());
cSetDcOptions(opts);
}
}
for (QSet<int32>::const_iterator i = restart.cbegin(), e = restart.cend(); i != e; ++i) {
MTP::restart(*i);
}
}
namespace {
QReadWriteLock _dcOptionsMutex;
}
QReadWriteLock *mtpDcOptionsMutex() {
return &_dcOptionsMutex;
}
MTProtoConfigLoader::MTProtoConfigLoader() : _enumCurrent(0), _enumRequest(0) {
connect(&_enumDCTimer, SIGNAL(timeout()), this, SLOT(enumDC()));
connect(this, SIGNAL(killCurrentSession(qint32,qint32)), this, SLOT(onKillCurrentSession(qint32,qint32)), Qt::QueuedConnection);
}
void MTProtoConfigLoader::load() {
@@ -189,23 +217,15 @@ void MTProtoConfigLoader::load() {
_enumDCTimer.start(MTPEnumDCTimeout);
}
void MTProtoConfigLoader::onKillCurrentSession(qint32 request, qint32 current) {
if (request == _enumRequest && current == _enumCurrent) {
if (_enumRequest) {
MTP::cancel(_enumRequest);
_enumRequest = 0;
}
if (_enumCurrent) {
MTP::killSession(MTP::cfg + _enumCurrent);
_enumCurrent = 0;
}
}
}
void MTProtoConfigLoader::done() {
_enumDCTimer.stop();
if (_enumRequest || _enumCurrent) {
emit killCurrentSession(_enumRequest, _enumCurrent);
if (_enumRequest) {
MTP::cancel(_enumRequest);
_enumRequest = 0;
}
if (_enumCurrent) {
MTP::killSession(MTP::cfg + _enumCurrent);
_enumCurrent = 0;
}
emit loaded();
}
@@ -220,14 +240,16 @@ void MTProtoConfigLoader::enumDC() {
} else {
MTP::killSession(MTP::cfg + _enumCurrent);
}
const mtpDcOptions &options(cDcOptions());
for (mtpDcOptions::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) {
if (i.key() == _enumCurrent) {
_enumCurrent = (++i == e) ? options.cbegin().key() : i.key();
break;
{
QReadLocker lock(mtpDcOptionsMutex());
const mtpDcOptions &options(cDcOptions());
for (mtpDcOptions::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) {
if (i.key() == _enumCurrent) {
_enumCurrent = (++i == e) ? options.cbegin().key() : i.key();
break;
}
}
}
_enumRequest = MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed), MTP::cfg + _enumCurrent);
_enumDCTimer.start(MTPEnumDCTimeout);

View File

@@ -72,12 +72,10 @@ public:
public slots:
void enumDC();
void onKillCurrentSession(qint32 request, qint32 session);
signals:
void loaded();
void killCurrentSession(qint32 request, qint32 session);
private:
@@ -104,3 +102,4 @@ mtpKeysMap mtpGetKeys();
void mtpSetKey(int32 dc, mtpAuthKeyPtr key);
void mtpUpdateDcOptions(const QVector<MTPDcOption> &options);
QReadWriteLock *mtpDcOptionsMutex();

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

@@ -3926,6 +3926,7 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
switch (stage) {
case 0: to.add(" alt: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" stickerset: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
@@ -4017,7 +4018,8 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
switch (stage) {
case 0: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" packs: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" documents: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" sets: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" documents: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
@@ -4289,6 +4291,68 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
break;
case mtpc_inputStickerSetEmpty:
to.add("{ inputStickerSetEmpty }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
break;
case mtpc_inputStickerSetID:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ inputStickerSetID");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_inputStickerSetShortName:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ inputStickerSetShortName");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" short_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_stickerSet:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ stickerSet");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" short_name: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_messages_stickerSet:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_stickerSet");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" set: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" packs: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" documents: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_req_pq:
if (stage) {
to.add(",\n").addSpaces(lev);
@@ -4779,6 +4843,32 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
break;
case mtpc_messages_installStickerSet:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_installStickerSet");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" stickerset: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_messages_uninstallStickerSet:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_uninstallStickerSet");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" stickerset: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_invokeAfterMsg:
if (stage) {
to.add(",\n").addSpaces(lev);
@@ -6027,6 +6117,19 @@ void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpP
}
break;
case mtpc_messages_getStickerSet:
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_getStickerSet");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" stickerset: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
break;
case mtpc_rpc_result:
if (stage) {
to.add(",\n").addSpaces(lev);

View File

@@ -340,7 +340,7 @@ enum {
mtpc_updateUserPhone = 0x12b9417b,
mtpc_documentAttributeImageSize = 0x6c37c15c,
mtpc_documentAttributeAnimated = 0x11b58939,
mtpc_documentAttributeSticker = 0x994c9882,
mtpc_documentAttributeSticker = 0x3a556302,
mtpc_documentAttributeVideo = 0x5910cccb,
mtpc_documentAttributeAudio = 0x51448e5,
mtpc_documentAttributeFilename = 0x15590068,
@@ -348,7 +348,7 @@ enum {
mtpc_messages_stickers = 0x8a8ecd32,
mtpc_stickerPack = 0x12b299d4,
mtpc_messages_allStickersNotModified = 0xe86602c3,
mtpc_messages_allStickers = 0xdcef3102,
mtpc_messages_allStickers = 0x5ce352ec,
mtpc_disabledFeature = 0xae636f24,
mtpc_updateReadHistoryInbox = 0x9961fd5c,
mtpc_updateReadHistoryOutbox = 0x2f2f21bf,
@@ -378,6 +378,11 @@ enum {
mtpc_chatInvite = 0xce917dcd,
mtpc_messageActionChatJoinedByLink = 0xf89cf5e8,
mtpc_updateReadMessagesContents = 0x68c13933,
mtpc_inputStickerSetEmpty = 0xffb62b95,
mtpc_inputStickerSetID = 0x9de7a269,
mtpc_inputStickerSetShortName = 0x861cc8a0,
mtpc_stickerSet = 0xa7a43b17,
mtpc_messages_stickerSet = 0xb60a24a6,
mtpc_invokeAfterMsg = 0xcb9f372d,
mtpc_invokeAfterMsgs = 0x3dc4b4f0,
mtpc_auth_checkPhone = 0x6fe51dfb,
@@ -500,7 +505,10 @@ enum {
mtpc_invokeWithoutUpdates = 0xbf9459b7,
mtpc_messages_exportChatInvite = 0x7d885289,
mtpc_messages_checkChatInvite = 0x3eadb1bb,
mtpc_messages_importChatInvite = 0x6c50051c
mtpc_messages_importChatInvite = 0x6c50051c,
mtpc_messages_getStickerSet = 0x2619a90e,
mtpc_messages_installStickerSet = 0xefbbfae9,
mtpc_messages_uninstallStickerSet = 0xf96e55de
};
// Type forward declarations
@@ -1045,6 +1053,16 @@ class MTPchatInvite;
class MTPDchatInviteAlready;
class MTPDchatInvite;
class MTPinputStickerSet;
class MTPDinputStickerSetID;
class MTPDinputStickerSetShortName;
class MTPstickerSet;
class MTPDstickerSet;
class MTPmessages_stickerSet;
class MTPDmessages_stickerSet;
// Boxed types definitions
typedef MTPBoxed<MTPresPQ> MTPResPQ;
@@ -1188,6 +1206,9 @@ typedef MTPBoxed<MTPauth_passwordRecovery> MTPauth_PasswordRecovery;
typedef MTPBoxed<MTPreceivedNotifyMessage> MTPReceivedNotifyMessage;
typedef MTPBoxed<MTPexportedChatInvite> MTPExportedChatInvite;
typedef MTPBoxed<MTPchatInvite> MTPChatInvite;
typedef MTPBoxed<MTPinputStickerSet> MTPInputStickerSet;
typedef MTPBoxed<MTPstickerSet> MTPStickerSet;
typedef MTPBoxed<MTPmessages_stickerSet> MTPmessages_StickerSet;
// Type classes definitions
@@ -7153,7 +7174,7 @@ private:
friend MTPdocumentAttribute MTP_documentAttributeImageSize(MTPint _w, MTPint _h);
friend MTPdocumentAttribute MTP_documentAttributeAnimated();
friend MTPdocumentAttribute MTP_documentAttributeSticker(const MTPstring &_alt);
friend MTPdocumentAttribute MTP_documentAttributeSticker(const MTPstring &_alt, const MTPInputStickerSet &_stickerset);
friend MTPdocumentAttribute MTP_documentAttributeVideo(MTPint _duration, MTPint _w, MTPint _h);
friend MTPdocumentAttribute MTP_documentAttributeAudio(MTPint _duration);
friend MTPdocumentAttribute MTP_documentAttributeFilename(const MTPstring &_file_name);
@@ -7263,7 +7284,7 @@ private:
explicit MTPmessages_allStickers(MTPDmessages_allStickers *_data);
friend MTPmessages_allStickers MTP_messages_allStickersNotModified();
friend MTPmessages_allStickers MTP_messages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents);
friend MTPmessages_allStickers MTP_messages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPStickerSet> &_sets, const MTPVector<MTPDocument> &_documents);
mtpTypeId _type;
};
@@ -7749,6 +7770,120 @@ private:
};
typedef MTPBoxed<MTPchatInvite> MTPChatInvite;
class MTPinputStickerSet : private mtpDataOwner {
public:
MTPinputStickerSet() : mtpDataOwner(0), _type(0) {
}
MTPinputStickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) : mtpDataOwner(0), _type(0) {
read(from, end, cons);
}
MTPDinputStickerSetID &_inputStickerSetID() {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_inputStickerSetID) throw mtpErrorWrongTypeId(_type, mtpc_inputStickerSetID);
split();
return *(MTPDinputStickerSetID*)data;
}
const MTPDinputStickerSetID &c_inputStickerSetID() const {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_inputStickerSetID) throw mtpErrorWrongTypeId(_type, mtpc_inputStickerSetID);
return *(const MTPDinputStickerSetID*)data;
}
MTPDinputStickerSetShortName &_inputStickerSetShortName() {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_inputStickerSetShortName) throw mtpErrorWrongTypeId(_type, mtpc_inputStickerSetShortName);
split();
return *(MTPDinputStickerSetShortName*)data;
}
const MTPDinputStickerSetShortName &c_inputStickerSetShortName() const {
if (!data) throw mtpErrorUninitialized();
if (_type != mtpc_inputStickerSetShortName) throw mtpErrorWrongTypeId(_type, mtpc_inputStickerSetShortName);
return *(const MTPDinputStickerSetShortName*)data;
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPinputStickerSet(mtpTypeId type);
explicit MTPinputStickerSet(MTPDinputStickerSetID *_data);
explicit MTPinputStickerSet(MTPDinputStickerSetShortName *_data);
friend MTPinputStickerSet MTP_inputStickerSetEmpty();
friend MTPinputStickerSet MTP_inputStickerSetID(const MTPlong &_id, const MTPlong &_access_hash);
friend MTPinputStickerSet MTP_inputStickerSetShortName(const MTPstring &_short_name);
mtpTypeId _type;
};
typedef MTPBoxed<MTPinputStickerSet> MTPInputStickerSet;
class MTPstickerSet : private mtpDataOwner {
public:
MTPstickerSet();
MTPstickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_stickerSet) : mtpDataOwner(0) {
read(from, end, cons);
}
MTPDstickerSet &_stickerSet() {
if (!data) throw mtpErrorUninitialized();
split();
return *(MTPDstickerSet*)data;
}
const MTPDstickerSet &c_stickerSet() const {
if (!data) throw mtpErrorUninitialized();
return *(const MTPDstickerSet*)data;
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_stickerSet);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPstickerSet(MTPDstickerSet *_data);
friend MTPstickerSet MTP_stickerSet(const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_short_name);
};
typedef MTPBoxed<MTPstickerSet> MTPStickerSet;
class MTPmessages_stickerSet : private mtpDataOwner {
public:
MTPmessages_stickerSet();
MTPmessages_stickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_stickerSet) : mtpDataOwner(0) {
read(from, end, cons);
}
MTPDmessages_stickerSet &_messages_stickerSet() {
if (!data) throw mtpErrorUninitialized();
split();
return *(MTPDmessages_stickerSet*)data;
}
const MTPDmessages_stickerSet &c_messages_stickerSet() const {
if (!data) throw mtpErrorUninitialized();
return *(const MTPDmessages_stickerSet*)data;
}
uint32 innerLength() const;
mtpTypeId type() const;
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_stickerSet);
void write(mtpBuffer &to) const;
typedef void ResponseType;
private:
explicit MTPmessages_stickerSet(MTPDmessages_stickerSet *_data);
friend MTPmessages_stickerSet MTP_messages_stickerSet(const MTPStickerSet &_set, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents);
};
typedef MTPBoxed<MTPmessages_stickerSet> MTPmessages_StickerSet;
// Type constructors with data
class MTPDresPQ : public mtpDataImpl<MTPDresPQ> {
@@ -8777,13 +8912,13 @@ public:
enum {
flag_fwd_from_id = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
};
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
};
class MTPDmessageService : public mtpDataImpl<MTPDmessageService> {
@@ -9886,13 +10021,13 @@ public:
enum {
flag_fwd_from_id = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
};
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
};
class MTPDupdateShortChatMessage : public mtpDataImpl<MTPDupdateShortChatMessage> {
@@ -9916,13 +10051,13 @@ public:
enum {
flag_fwd_from_id = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
flag_fwd_date = (1 << 2),
flag_reply_to_msg_id = (1 << 3),
};
bool has_fwd_from_id() const { return vflags.v & flag_fwd_from_id; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
bool has_fwd_date() const { return vflags.v & flag_fwd_date; }
bool has_reply_to_msg_id() const { return vflags.v & flag_reply_to_msg_id; }
};
class MTPDupdateShort : public mtpDataImpl<MTPDupdateShort> {
@@ -10630,10 +10765,11 @@ class MTPDdocumentAttributeSticker : public mtpDataImpl<MTPDdocumentAttributeSti
public:
MTPDdocumentAttributeSticker() {
}
MTPDdocumentAttributeSticker(const MTPstring &_alt) : valt(_alt) {
MTPDdocumentAttributeSticker(const MTPstring &_alt, const MTPInputStickerSet &_stickerset) : valt(_alt), vstickerset(_stickerset) {
}
MTPstring valt;
MTPInputStickerSet vstickerset;
};
class MTPDdocumentAttributeVideo : public mtpDataImpl<MTPDdocumentAttributeVideo> {
@@ -10694,11 +10830,12 @@ class MTPDmessages_allStickers : public mtpDataImpl<MTPDmessages_allStickers> {
public:
MTPDmessages_allStickers() {
}
MTPDmessages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents) : vhash(_hash), vpacks(_packs), vdocuments(_documents) {
MTPDmessages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPStickerSet> &_sets, const MTPVector<MTPDocument> &_documents) : vhash(_hash), vpacks(_packs), vsets(_sets), vdocuments(_documents) {
}
MTPstring vhash;
MTPVector<MTPStickerPack> vpacks;
MTPVector<MTPStickerSet> vsets;
MTPVector<MTPDocument> vdocuments;
};
@@ -10769,30 +10906,30 @@ public:
MTPstring vauthor;
enum {
flag_photo = (1 << 4),
flag_embed_type = (1 << 5),
flag_author = (1 << 8),
flag_embed_width = (1 << 6),
flag_type = (1 << 0),
flag_duration = (1 << 7),
flag_embed_height = (1 << 6),
flag_description = (1 << 3),
flag_site_name = (1 << 1),
flag_title = (1 << 2),
flag_description = (1 << 3),
flag_photo = (1 << 4),
flag_embed_url = (1 << 5),
flag_embed_type = (1 << 5),
flag_embed_width = (1 << 6),
flag_embed_height = (1 << 6),
flag_duration = (1 << 7),
flag_author = (1 << 8),
};
bool has_photo() const { return vflags.v & flag_photo; }
bool has_embed_type() const { return vflags.v & flag_embed_type; }
bool has_author() const { return vflags.v & flag_author; }
bool has_embed_width() const { return vflags.v & flag_embed_width; }
bool has_type() const { return vflags.v & flag_type; }
bool has_duration() const { return vflags.v & flag_duration; }
bool has_embed_height() const { return vflags.v & flag_embed_height; }
bool has_description() const { return vflags.v & flag_description; }
bool has_site_name() const { return vflags.v & flag_site_name; }
bool has_title() const { return vflags.v & flag_title; }
bool has_description() const { return vflags.v & flag_description; }
bool has_photo() const { return vflags.v & flag_photo; }
bool has_embed_url() const { return vflags.v & flag_embed_url; }
bool has_embed_type() const { return vflags.v & flag_embed_type; }
bool has_embed_width() const { return vflags.v & flag_embed_width; }
bool has_embed_height() const { return vflags.v & flag_embed_height; }
bool has_duration() const { return vflags.v & flag_duration; }
bool has_author() const { return vflags.v & flag_author; }
};
class MTPDauthorization : public mtpDataImpl<MTPDauthorization> {
@@ -10877,15 +11014,15 @@ public:
enum {
flag_new_salt = (1 << 0),
flag_email = (1 << 1),
flag_new_password_hash = (1 << 0),
flag_hint = (1 << 0),
flag_email = (1 << 1),
};
bool has_new_salt() const { return vflags.v & flag_new_salt; }
bool has_email() const { return vflags.v & flag_email; }
bool has_new_password_hash() const { return vflags.v & flag_new_password_hash; }
bool has_hint() const { return vflags.v & flag_hint; }
bool has_email() const { return vflags.v & flag_email; }
};
class MTPDauth_passwordRecovery : public mtpDataImpl<MTPDauth_passwordRecovery> {
@@ -10939,6 +11076,52 @@ public:
MTPstring vtitle;
};
class MTPDinputStickerSetID : public mtpDataImpl<MTPDinputStickerSetID> {
public:
MTPDinputStickerSetID() {
}
MTPDinputStickerSetID(const MTPlong &_id, const MTPlong &_access_hash) : vid(_id), vaccess_hash(_access_hash) {
}
MTPlong vid;
MTPlong vaccess_hash;
};
class MTPDinputStickerSetShortName : public mtpDataImpl<MTPDinputStickerSetShortName> {
public:
MTPDinputStickerSetShortName() {
}
MTPDinputStickerSetShortName(const MTPstring &_short_name) : vshort_name(_short_name) {
}
MTPstring vshort_name;
};
class MTPDstickerSet : public mtpDataImpl<MTPDstickerSet> {
public:
MTPDstickerSet() {
}
MTPDstickerSet(const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_short_name) : vid(_id), vaccess_hash(_access_hash), vtitle(_title), vshort_name(_short_name) {
}
MTPlong vid;
MTPlong vaccess_hash;
MTPstring vtitle;
MTPstring vshort_name;
};
class MTPDmessages_stickerSet : public mtpDataImpl<MTPDmessages_stickerSet> {
public:
MTPDmessages_stickerSet() {
}
MTPDmessages_stickerSet(const MTPStickerSet &_set, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents) : vset(_set), vpacks(_packs), vdocuments(_documents) {
}
MTPStickerSet vset;
MTPVector<MTPStickerPack> vpacks;
MTPVector<MTPDocument> vdocuments;
};
// RPC methods
class MTPreq_pq { // RPC method 'req_pq'
@@ -16433,6 +16616,123 @@ public:
}
};
class MTPmessages_getStickerSet { // RPC method 'messages.getStickerSet'
public:
MTPInputStickerSet vstickerset;
MTPmessages_getStickerSet() {
}
MTPmessages_getStickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getStickerSet) {
read(from, end, cons);
}
MTPmessages_getStickerSet(const MTPInputStickerSet &_stickerset) : vstickerset(_stickerset) {
}
uint32 innerLength() const {
return vstickerset.innerLength();
}
mtpTypeId type() const {
return mtpc_messages_getStickerSet;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getStickerSet) {
vstickerset.read(from, end);
}
void write(mtpBuffer &to) const {
vstickerset.write(to);
}
typedef MTPmessages_StickerSet ResponseType;
};
class MTPmessages_GetStickerSet : public MTPBoxed<MTPmessages_getStickerSet> {
public:
MTPmessages_GetStickerSet() {
}
MTPmessages_GetStickerSet(const MTPmessages_getStickerSet &v) : MTPBoxed<MTPmessages_getStickerSet>(v) {
}
MTPmessages_GetStickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_getStickerSet>(from, end, cons) {
}
MTPmessages_GetStickerSet(const MTPInputStickerSet &_stickerset) : MTPBoxed<MTPmessages_getStickerSet>(MTPmessages_getStickerSet(_stickerset)) {
}
};
class MTPmessages_installStickerSet { // RPC method 'messages.installStickerSet'
public:
MTPInputStickerSet vstickerset;
MTPmessages_installStickerSet() {
}
MTPmessages_installStickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_installStickerSet) {
read(from, end, cons);
}
MTPmessages_installStickerSet(const MTPInputStickerSet &_stickerset) : vstickerset(_stickerset) {
}
uint32 innerLength() const {
return vstickerset.innerLength();
}
mtpTypeId type() const {
return mtpc_messages_installStickerSet;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_installStickerSet) {
vstickerset.read(from, end);
}
void write(mtpBuffer &to) const {
vstickerset.write(to);
}
typedef MTPBool ResponseType;
};
class MTPmessages_InstallStickerSet : public MTPBoxed<MTPmessages_installStickerSet> {
public:
MTPmessages_InstallStickerSet() {
}
MTPmessages_InstallStickerSet(const MTPmessages_installStickerSet &v) : MTPBoxed<MTPmessages_installStickerSet>(v) {
}
MTPmessages_InstallStickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_installStickerSet>(from, end, cons) {
}
MTPmessages_InstallStickerSet(const MTPInputStickerSet &_stickerset) : MTPBoxed<MTPmessages_installStickerSet>(MTPmessages_installStickerSet(_stickerset)) {
}
};
class MTPmessages_uninstallStickerSet { // RPC method 'messages.uninstallStickerSet'
public:
MTPInputStickerSet vstickerset;
MTPmessages_uninstallStickerSet() {
}
MTPmessages_uninstallStickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_uninstallStickerSet) {
read(from, end, cons);
}
MTPmessages_uninstallStickerSet(const MTPInputStickerSet &_stickerset) : vstickerset(_stickerset) {
}
uint32 innerLength() const {
return vstickerset.innerLength();
}
mtpTypeId type() const {
return mtpc_messages_uninstallStickerSet;
}
void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_uninstallStickerSet) {
vstickerset.read(from, end);
}
void write(mtpBuffer &to) const {
vstickerset.write(to);
}
typedef MTPBool ResponseType;
};
class MTPmessages_UninstallStickerSet : public MTPBoxed<MTPmessages_uninstallStickerSet> {
public:
MTPmessages_UninstallStickerSet() {
}
MTPmessages_UninstallStickerSet(const MTPmessages_uninstallStickerSet &v) : MTPBoxed<MTPmessages_uninstallStickerSet>(v) {
}
MTPmessages_UninstallStickerSet(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_uninstallStickerSet>(from, end, cons) {
}
MTPmessages_UninstallStickerSet(const MTPInputStickerSet &_stickerset) : MTPBoxed<MTPmessages_uninstallStickerSet>(MTPmessages_uninstallStickerSet(_stickerset)) {
}
};
// Inline methods definition
inline MTPresPQ::MTPresPQ() : mtpDataOwner(new MTPDresPQ()) {
@@ -24350,7 +24650,7 @@ inline uint32 MTPdocumentAttribute::innerLength() const {
}
case mtpc_documentAttributeSticker: {
const MTPDdocumentAttributeSticker &v(c_documentAttributeSticker());
return v.valt.innerLength();
return v.valt.innerLength() + v.vstickerset.innerLength();
}
case mtpc_documentAttributeVideo: {
const MTPDdocumentAttributeVideo &v(c_documentAttributeVideo());
@@ -24385,6 +24685,7 @@ inline void MTPdocumentAttribute::read(const mtpPrime *&from, const mtpPrime *en
if (!data) setData(new MTPDdocumentAttributeSticker());
MTPDdocumentAttributeSticker &v(_documentAttributeSticker());
v.valt.read(from, end);
v.vstickerset.read(from, end);
} break;
case mtpc_documentAttributeVideo: _type = cons; {
if (!data) setData(new MTPDdocumentAttributeVideo());
@@ -24416,6 +24717,7 @@ inline void MTPdocumentAttribute::write(mtpBuffer &to) const {
case mtpc_documentAttributeSticker: {
const MTPDdocumentAttributeSticker &v(c_documentAttributeSticker());
v.valt.write(to);
v.vstickerset.write(to);
} break;
case mtpc_documentAttributeVideo: {
const MTPDdocumentAttributeVideo &v(c_documentAttributeVideo());
@@ -24460,8 +24762,8 @@ inline MTPdocumentAttribute MTP_documentAttributeImageSize(MTPint _w, MTPint _h)
inline MTPdocumentAttribute MTP_documentAttributeAnimated() {
return MTPdocumentAttribute(mtpc_documentAttributeAnimated);
}
inline MTPdocumentAttribute MTP_documentAttributeSticker(const MTPstring &_alt) {
return MTPdocumentAttribute(new MTPDdocumentAttributeSticker(_alt));
inline MTPdocumentAttribute MTP_documentAttributeSticker(const MTPstring &_alt, const MTPInputStickerSet &_stickerset) {
return MTPdocumentAttribute(new MTPDdocumentAttributeSticker(_alt, _stickerset));
}
inline MTPdocumentAttribute MTP_documentAttributeVideo(MTPint _duration, MTPint _w, MTPint _h) {
return MTPdocumentAttribute(new MTPDdocumentAttributeVideo(_duration, _w, _h));
@@ -24557,7 +24859,7 @@ inline uint32 MTPmessages_allStickers::innerLength() const {
switch (_type) {
case mtpc_messages_allStickers: {
const MTPDmessages_allStickers &v(c_messages_allStickers());
return v.vhash.innerLength() + v.vpacks.innerLength() + v.vdocuments.innerLength();
return v.vhash.innerLength() + v.vpacks.innerLength() + v.vsets.innerLength() + v.vdocuments.innerLength();
}
}
return 0;
@@ -24575,6 +24877,7 @@ inline void MTPmessages_allStickers::read(const mtpPrime *&from, const mtpPrime
MTPDmessages_allStickers &v(_messages_allStickers());
v.vhash.read(from, end);
v.vpacks.read(from, end);
v.vsets.read(from, end);
v.vdocuments.read(from, end);
} break;
default: throw mtpErrorUnexpected(cons, "MTPmessages_allStickers");
@@ -24586,6 +24889,7 @@ inline void MTPmessages_allStickers::write(mtpBuffer &to) const {
const MTPDmessages_allStickers &v(c_messages_allStickers());
v.vhash.write(to);
v.vpacks.write(to);
v.vsets.write(to);
v.vdocuments.write(to);
} break;
}
@@ -24602,8 +24906,8 @@ inline MTPmessages_allStickers::MTPmessages_allStickers(MTPDmessages_allStickers
inline MTPmessages_allStickers MTP_messages_allStickersNotModified() {
return MTPmessages_allStickers(mtpc_messages_allStickersNotModified);
}
inline MTPmessages_allStickers MTP_messages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents) {
return MTPmessages_allStickers(new MTPDmessages_allStickers(_hash, _packs, _documents));
inline MTPmessages_allStickers MTP_messages_allStickers(const MTPstring &_hash, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPStickerSet> &_sets, const MTPVector<MTPDocument> &_documents) {
return MTPmessages_allStickers(new MTPDmessages_allStickers(_hash, _packs, _sets, _documents));
}
inline MTPdisabledFeature::MTPdisabledFeature() : mtpDataOwner(new MTPDdisabledFeature()) {
@@ -25199,6 +25503,140 @@ inline MTPchatInvite MTP_chatInvite(const MTPstring &_title) {
return MTPchatInvite(new MTPDchatInvite(_title));
}
inline uint32 MTPinputStickerSet::innerLength() const {
switch (_type) {
case mtpc_inputStickerSetID: {
const MTPDinputStickerSetID &v(c_inputStickerSetID());
return v.vid.innerLength() + v.vaccess_hash.innerLength();
}
case mtpc_inputStickerSetShortName: {
const MTPDinputStickerSetShortName &v(c_inputStickerSetShortName());
return v.vshort_name.innerLength();
}
}
return 0;
}
inline mtpTypeId MTPinputStickerSet::type() const {
if (!_type) throw mtpErrorUninitialized();
return _type;
}
inline void MTPinputStickerSet::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
if (cons != _type) setData(0);
switch (cons) {
case mtpc_inputStickerSetEmpty: _type = cons; break;
case mtpc_inputStickerSetID: _type = cons; {
if (!data) setData(new MTPDinputStickerSetID());
MTPDinputStickerSetID &v(_inputStickerSetID());
v.vid.read(from, end);
v.vaccess_hash.read(from, end);
} break;
case mtpc_inputStickerSetShortName: _type = cons; {
if (!data) setData(new MTPDinputStickerSetShortName());
MTPDinputStickerSetShortName &v(_inputStickerSetShortName());
v.vshort_name.read(from, end);
} break;
default: throw mtpErrorUnexpected(cons, "MTPinputStickerSet");
}
}
inline void MTPinputStickerSet::write(mtpBuffer &to) const {
switch (_type) {
case mtpc_inputStickerSetID: {
const MTPDinputStickerSetID &v(c_inputStickerSetID());
v.vid.write(to);
v.vaccess_hash.write(to);
} break;
case mtpc_inputStickerSetShortName: {
const MTPDinputStickerSetShortName &v(c_inputStickerSetShortName());
v.vshort_name.write(to);
} break;
}
}
inline MTPinputStickerSet::MTPinputStickerSet(mtpTypeId type) : mtpDataOwner(0), _type(type) {
switch (type) {
case mtpc_inputStickerSetEmpty: break;
case mtpc_inputStickerSetID: setData(new MTPDinputStickerSetID()); break;
case mtpc_inputStickerSetShortName: setData(new MTPDinputStickerSetShortName()); break;
default: throw mtpErrorBadTypeId(type, "MTPinputStickerSet");
}
}
inline MTPinputStickerSet::MTPinputStickerSet(MTPDinputStickerSetID *_data) : mtpDataOwner(_data), _type(mtpc_inputStickerSetID) {
}
inline MTPinputStickerSet::MTPinputStickerSet(MTPDinputStickerSetShortName *_data) : mtpDataOwner(_data), _type(mtpc_inputStickerSetShortName) {
}
inline MTPinputStickerSet MTP_inputStickerSetEmpty() {
return MTPinputStickerSet(mtpc_inputStickerSetEmpty);
}
inline MTPinputStickerSet MTP_inputStickerSetID(const MTPlong &_id, const MTPlong &_access_hash) {
return MTPinputStickerSet(new MTPDinputStickerSetID(_id, _access_hash));
}
inline MTPinputStickerSet MTP_inputStickerSetShortName(const MTPstring &_short_name) {
return MTPinputStickerSet(new MTPDinputStickerSetShortName(_short_name));
}
inline MTPstickerSet::MTPstickerSet() : mtpDataOwner(new MTPDstickerSet()) {
}
inline uint32 MTPstickerSet::innerLength() const {
const MTPDstickerSet &v(c_stickerSet());
return v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vtitle.innerLength() + v.vshort_name.innerLength();
}
inline mtpTypeId MTPstickerSet::type() const {
return mtpc_stickerSet;
}
inline void MTPstickerSet::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
if (cons != mtpc_stickerSet) throw mtpErrorUnexpected(cons, "MTPstickerSet");
if (!data) setData(new MTPDstickerSet());
MTPDstickerSet &v(_stickerSet());
v.vid.read(from, end);
v.vaccess_hash.read(from, end);
v.vtitle.read(from, end);
v.vshort_name.read(from, end);
}
inline void MTPstickerSet::write(mtpBuffer &to) const {
const MTPDstickerSet &v(c_stickerSet());
v.vid.write(to);
v.vaccess_hash.write(to);
v.vtitle.write(to);
v.vshort_name.write(to);
}
inline MTPstickerSet::MTPstickerSet(MTPDstickerSet *_data) : mtpDataOwner(_data) {
}
inline MTPstickerSet MTP_stickerSet(const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_title, const MTPstring &_short_name) {
return MTPstickerSet(new MTPDstickerSet(_id, _access_hash, _title, _short_name));
}
inline MTPmessages_stickerSet::MTPmessages_stickerSet() : mtpDataOwner(new MTPDmessages_stickerSet()) {
}
inline uint32 MTPmessages_stickerSet::innerLength() const {
const MTPDmessages_stickerSet &v(c_messages_stickerSet());
return v.vset.innerLength() + v.vpacks.innerLength() + v.vdocuments.innerLength();
}
inline mtpTypeId MTPmessages_stickerSet::type() const {
return mtpc_messages_stickerSet;
}
inline void MTPmessages_stickerSet::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) {
if (cons != mtpc_messages_stickerSet) throw mtpErrorUnexpected(cons, "MTPmessages_stickerSet");
if (!data) setData(new MTPDmessages_stickerSet());
MTPDmessages_stickerSet &v(_messages_stickerSet());
v.vset.read(from, end);
v.vpacks.read(from, end);
v.vdocuments.read(from, end);
}
inline void MTPmessages_stickerSet::write(mtpBuffer &to) const {
const MTPDmessages_stickerSet &v(c_messages_stickerSet());
v.vset.write(to);
v.vpacks.write(to);
v.vdocuments.write(to);
}
inline MTPmessages_stickerSet::MTPmessages_stickerSet(MTPDmessages_stickerSet *_data) : mtpDataOwner(_data) {
}
inline MTPmessages_stickerSet MTP_messages_stickerSet(const MTPStickerSet &_set, const MTPVector<MTPStickerPack> &_packs, const MTPVector<MTPDocument> &_documents) {
return MTPmessages_stickerSet(new MTPDmessages_stickerSet(_set, _packs, _documents));
}
// Human-readable text serialization
#if (defined _DEBUG || defined _WITH_DEBUG)

View File

@@ -63,11 +63,15 @@ void MTPSessionData::clear() {
}
MTProtoSession::MTProtoSession() : data(this), dcId(0), dc(0), msSendCall(0), msWait(0), _ping(false) {
MTProtoSession::MTProtoSession() : _killed(false), data(this), dcWithShift(0), dc(0), msSendCall(0), msWait(0), _ping(false) {
}
void MTProtoSession::start(int32 dcenter) {
if (dcId) {
if (_killed) {
DEBUG_LOG(("Session Error: can't start a killed session"));
return;
}
if (dcWithShift) {
DEBUG_LOG(("Session Info: MTProtoSession::start called on already started session"));
return;
}
@@ -84,8 +88,8 @@ void MTProtoSession::start(int32 dcenter) {
connections.reserve(cConnectionsInSession());
for (uint32 i = 0; i < cConnectionsInSession(); ++i) {
connections.push_back(new MTProtoConnection());
dcId = connections.back()->start(&data, dcenter);
if (!dcId) {
dcWithShift = connections.back()->start(&data, dcenter);
if (!dcWithShift) {
for (MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); j != e; ++j) {
delete *j;
}
@@ -94,11 +98,12 @@ void MTProtoSession::start(int32 dcenter) {
return;
}
if (!dc) {
dcenter = dcId;
MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId % _mtp_internal::dcShift);
dcenter = dcWithShift;
int32 dcId = dcWithShift % _mtp_internal::dcShift;
MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId);
if (dcIndex == dcs.cend()) {
dc = MTProtoDCPtr(new MTProtoDC(dcId % _mtp_internal::dcShift, mtpAuthKeyPtr()));
dcs.insert(dcId % _mtp_internal::dcShift, dc);
dc = MTProtoDCPtr(new MTProtoDC(dcId, mtpAuthKeyPtr()));
dcs.insert(dcWithShift % _mtp_internal::dcShift, dc);
} else {
dc = dcIndex.value();
}
@@ -115,18 +120,32 @@ void MTProtoSession::start(int32 dcenter) {
}
void MTProtoSession::restart() {
if (_killed) {
DEBUG_LOG(("Session Error: can't restart a killed session"));
return;
}
emit needToRestart();
}
void MTProtoSession::stop() {
DEBUG_LOG(("Session Info: stopping session %1").arg(dcId));
DEBUG_LOG(("Session Info: stopping session dcWithShift %1").arg(dcWithShift));
while (!connections.isEmpty()) {
connections.back()->stop();
connections.pop_back();
}
}
void MTProtoSession::kill() {
stop();
_killed = true;
DEBUG_LOG(("Session Info: marked session dcWithShift %1 as killed").arg(dcWithShift));
}
void MTProtoSession::sendAnything(quint64 msCanWait) {
if (_killed) {
DEBUG_LOG(("Session Error: can't send anything in a killed session"));
return;
}
uint64 ms = getms(true);
if (msSendCall) {
if (ms > msSendCall + msWait) {
@@ -141,11 +160,11 @@ void MTProtoSession::sendAnything(quint64 msCanWait) {
msWait = msCanWait;
}
if (msWait) {
DEBUG_LOG(("MTP Info: dc %1 can wait for %2ms from current %3").arg(dcId).arg(msWait).arg(msSendCall));
DEBUG_LOG(("MTP Info: dcWithShift %1 can wait for %2ms from current %3").arg(dcWithShift).arg(msWait).arg(msSendCall));
msSendCall = ms;
sender.start(msWait);
} else {
DEBUG_LOG(("MTP Info: dc %1 stopped send timer, can wait for %2ms from current %3").arg(dcId).arg(msWait).arg(msSendCall));
DEBUG_LOG(("MTP Info: dcWithShift %1 stopped send timer, can wait for %2ms from current %3").arg(dcWithShift).arg(msWait).arg(msSendCall));
sender.stop();
msSendCall = 0;
needToResumeAndSend();
@@ -153,20 +172,24 @@ void MTProtoSession::sendAnything(quint64 msCanWait) {
}
void MTProtoSession::needToResumeAndSend() {
if (_killed) {
DEBUG_LOG(("Session Info: can't resume a killed session"));
return;
}
if (connections.isEmpty()) {
DEBUG_LOG(("Session Info: resuming session %1").arg(dcId));
DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(dcWithShift));
MTProtoDCMap &dcs(mtpDCMap());
connections.reserve(cConnectionsInSession());
for (uint32 i = 0; i < cConnectionsInSession(); ++i) {
connections.push_back(new MTProtoConnection());
if (!connections.back()->start(&data, dcId)) {
if (!connections.back()->start(&data, dcWithShift)) {
for (MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); j != e; ++j) {
delete *j;
}
connections.clear();
DEBUG_LOG(("Session Info: could not start connection %1 to dc %2").arg(i).arg(dcId));
dcId = 0;
DEBUG_LOG(("Session Info: could not start connection %1 to dcWithShift %2").arg(i).arg(dcWithShift));
dcWithShift = 0;
return;
}
}
@@ -259,11 +282,11 @@ void MTProtoSession::checkRequestsByTimer() {
}
void MTProtoSession::onConnectionStateChange(qint32 newState) {
_mtp_internal::onStateChange(dcId, newState);
_mtp_internal::onStateChange(dcWithShift, newState);
}
void MTProtoSession::onResetDone() {
_mtp_internal::onSessionReset(dcId);
_mtp_internal::onSessionReset(dcWithShift);
}
void MTProtoSession::cancel(mtpRequestId requestId, mtpMsgId msgId) {
@@ -428,23 +451,23 @@ QReadWriteLock *MTProtoSession::keyMutex() const {
}
void MTProtoSession::authKeyCreatedForDC() {
DEBUG_LOG(("AuthKey Info: MTProtoSession::authKeyCreatedForDC slot, emitting authKeyCreated(), dc %1").arg(dcId));
DEBUG_LOG(("AuthKey Info: MTProtoSession::authKeyCreatedForDC slot, emitting authKeyCreated(), dcWithShift %1").arg(dcWithShift));
data.setKey(dc->getKey());
emit authKeyCreated();
}
void MTProtoSession::notifyKeyCreated(const mtpAuthKeyPtr &key) {
DEBUG_LOG(("AuthKey Info: MTProtoSession::keyCreated(), setting, dc %1").arg(dcId));
DEBUG_LOG(("AuthKey Info: MTProtoSession::keyCreated(), setting, dcWithShift %1").arg(dcWithShift));
dc->setKey(key);
}
void MTProtoSession::layerWasInitedForDC(bool wasInited) {
DEBUG_LOG(("MTP Info: MTProtoSession::layerWasInitedForDC slot, dc %1").arg(dcId));
DEBUG_LOG(("MTP Info: MTProtoSession::layerWasInitedForDC slot, dcWithShift %1").arg(dcWithShift));
data.setLayerWasInited(wasInited);
}
void MTProtoSession::notifyLayerInited(bool wasInited) {
DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dc %2").arg(logBool(wasInited)).arg(dcId));
DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dcWithShift %2").arg(logBool(wasInited)).arg(dcWithShift));
dc->setConnectionInited(wasInited);
emit dc->layerWasInited(wasInited);
}
@@ -453,7 +476,7 @@ void MTProtoSession::destroyKey() {
if (!dc) return;
if (data.getKey()) {
DEBUG_LOG(("MTP Info: destroying auth_key for dc %1").arg(dcId));
DEBUG_LOG(("MTP Info: destroying auth_key for dcWithShift %1").arg(dcWithShift));
if (data.getKey() == dc->getKey()) {
dc->destroyKey();
}
@@ -461,8 +484,8 @@ void MTProtoSession::destroyKey() {
}
}
int32 MTProtoSession::getDC() const {
return dcId;
int32 MTProtoSession::getDcWithShift() const {
return dcWithShift;
}
void MTProtoSession::tryToReceive() {
@@ -481,7 +504,7 @@ void MTProtoSession::tryToReceive() {
responses.erase(i);
}
if (requestId <= 0) {
if (dcId < int(_mtp_internal::dcShift)) { // call globalCallback only in main session
if (dcWithShift < int(_mtp_internal::dcShift)) { // call globalCallback only in main session
_mtp_internal::globalCallback(response.constData(), response.constData() + response.size());
}
} else {

View File

@@ -224,8 +224,9 @@ public:
void start(int32 dcenter = 0);
void restart();
void stop();
void kill();
int32 getDC() const;
int32 getDcWithShift() const;
~MTProtoSession();
QReadWriteLock *keyMutex() const;
@@ -275,10 +276,12 @@ private:
typedef QList<MTProtoConnection*> MTProtoConnections;
MTProtoConnections connections;
bool _killed;
MTPSessionData data;
int32 dcId;
int32 dcWithShift;
MTProtoDCPtr dc;
uint64 msSendCall, msWait;

View File

@@ -37,6 +37,6 @@ mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler ca
requestId = 0;
_mtp_internal::rpcErrorOccured(requestId, callbacks, rpcClientError("NO_REQUEST_ID", QString("send() failed to queue request, exception: %1").arg(e.what())));
}
if (requestId) _mtp_internal::registerRequest(requestId, toMainDC ? -getDC() : getDC());
if (requestId) _mtp_internal::registerRequest(requestId, toMainDC ? -getDcWithShift() : getDcWithShift());
return requestId;
}

View File

@@ -526,7 +526,7 @@ updateUserPhone#12b9417b user_id:int phone:string = Update;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#994c9882 alt:string = DocumentAttribute;
documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio#51448e5 duration:int = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
@@ -537,7 +537,7 @@ messages.stickers#8a8ecd32 hash:string stickers:Vector<Document> = messages.Stic
stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack;
messages.allStickersNotModified#e86602c3 = messages.AllStickers;
messages.allStickers#dcef3102 hash:string packs:Vector<StickerPack> documents:Vector<Document> = messages.AllStickers;
messages.allStickers#5ce352ec hash:string packs:Vector<StickerPack> sets:Vector<StickerSet> documents:Vector<Document> = messages.AllStickers;
disabledFeature#ae636f24 feature:string description:string = DisabledFeature;
@@ -588,6 +588,14 @@ messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction;
updateReadMessagesContents#68c13933 messages:Vector<int> pts:int pts_count:int = Update;
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
stickerSet#a7a43b17 id:long access_hash:long title:string short_name:string = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -744,4 +752,7 @@ invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#6c50051c hash:string = Updates;
messages.importChatInvite#6c50051c hash:string = Updates;
messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
messages.installStickerSet#efbbfae9 stickerset:InputStickerSet = Bool;
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;

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;
}
@@ -558,9 +558,9 @@ 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) {
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) {
@@ -574,7 +574,11 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
case mtpc_documentAttributeSticker: {
const MTPDdocumentAttributeSticker &d(attributes[i].c_documentAttributeSticker());
if (type == FileDocument) type = StickerDocument;
alt = qs(d.valt);
if (type == StickerDocument && !sticker) sticker = new StickerData();
if (sticker) {
sticker->alt = qs(d.valt);
sticker->set = d.vstickerset;
}
} break;
case mtpc_documentAttributeVideo: {
const MTPDdocumentAttributeVideo &d(attributes[i].c_documentAttributeVideo());
@@ -603,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();
@@ -402,6 +402,16 @@ public:
void onClick(Qt::MouseButton button) const;
};
struct StickerData {
StickerData() : set(MTP_inputStickerSetEmpty()) {
}
ImagePtr img;
QString alt;
MTPInputStickerSet set;
StorageImageLocation loc; // doc thumb location
};
enum DocumentType {
FileDocument,
VideoDocument,
@@ -415,7 +425,7 @@ struct DocumentData {
void forget() {
thumb->forget();
sticker->forget();
if (sticker) sticker->img->forget();
replyPreview->forget();
}
@@ -437,13 +447,16 @@ 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();
loader->rpcInvalidate();
loader = 0;
}
~DocumentData() {
delete sticker;
}
QString already(bool check = false);
@@ -453,7 +466,7 @@ struct DocumentData {
int32 duration;
uint64 access;
int32 date;
QString name, mime, alt; // alt - for stickers
QString name, mime;
ImagePtr thumb, replyPreview;
int32 dc;
int32 size;
@@ -466,7 +479,7 @@ struct DocumentData {
FileLocation location;
QByteArray data;
ImagePtr sticker;
StickerData *sticker;
int32 md5[8];
};

View File

@@ -149,7 +149,39 @@ MTPint toServerTime(const int32 &clientTime) {
// Precise timing functions / rand init
struct CRYPTO_dynlock_value {
QMutex mutex;
};
namespace {
bool _sslInited = false;
QMutex *_sslLocks = 0;
void _sslLockingCallback(int mode, int type, const char *file, int line) {
if (!_sslLocks) return; // not inited
if (mode & CRYPTO_LOCK) {
_sslLocks[type].lock();
} else {
_sslLocks[type].unlock();
}
}
void _sslThreadId(CRYPTO_THREADID *id) {
CRYPTO_THREADID_set_pointer(id, QThread::currentThreadId());
}
CRYPTO_dynlock_value *_sslCreateFunction(const char *file, int line) {
return new CRYPTO_dynlock_value();
}
void _sslLockFunction(int mode, CRYPTO_dynlock_value *l, const char *file, int line) {
if (mode & CRYPTO_LOCK) {
l->mutex.lock();
} else {
l->mutex.unlock();
}
}
void _sslDestroyFunction(CRYPTO_dynlock_value *l, const char *file, int line) {
delete l;
}
float64 _msFreq;
float64 _msgIdCoef;
int64 _msStart = 0, _msAddToMsStart = 0, _msAddToUnixtime = 0;
@@ -185,16 +217,6 @@ namespace {
#endif
_timeStart = myunixtime();
srand((uint32)(_msStart & 0xFFFFFFFFL));
if (!RAND_status()) { // should be always inited in all modern OS
char buf[16];
memcpy(buf, &_msStart, 8);
memcpy(buf + 8, &_msFreq, 8);
uchar sha256Buffer[32];
RAND_seed(hashSha256(buf, 16, sha256Buffer), 32);
if (!RAND_status()) {
LOG(("MTP Error: Could not init OpenSSL rand, RAND_status() is 0.."));
}
}
}
};
@@ -211,6 +233,38 @@ namespace {
_MsStarter _msStarter;
}
InitOpenSSL::InitOpenSSL() {
if (!RAND_status()) { // should be always inited in all modern OS
char buf[16];
memcpy(buf, &_msStart, 8);
memcpy(buf + 8, &_msFreq, 8);
uchar sha256Buffer[32];
RAND_seed(hashSha256(buf, 16, sha256Buffer), 32);
if (!RAND_status()) {
LOG(("MTP Error: Could not init OpenSSL rand, RAND_status() is 0.."));
}
}
int32 numLocks = CRYPTO_num_locks();
if (numLocks) {
_sslLocks = new QMutex[numLocks];
CRYPTO_set_locking_callback(_sslLockingCallback);
} else {
LOG(("MTP Error: Could not init OpenSSL threads, CRYPTO_num_locks() returned zero!"));
}
CRYPTO_THREADID_set_callback(_sslThreadId);
CRYPTO_set_dynlock_create_callback(_sslCreateFunction);
CRYPTO_set_dynlock_lock_callback(_sslLockFunction);
CRYPTO_set_dynlock_destroy_callback(_sslDestroyFunction);
_sslInited = true;
}
InitOpenSSL::~InitOpenSSL() {
delete[] _sslLocks;
_sslLocks = 0;
}
bool checkms() {
int64 unixms = (myunixtime() - _timeStart) * 1000LL + _msAddToUnixtime;
int64 ms = int64(getms(true));
@@ -578,7 +632,10 @@ char *hashMd5Hex(const int32 *hashmd5, void *dest) {
}
void memset_rand(void *data, uint32 len) {
_msInitialize();
if (!_sslInited) {
LOG(("Critical Error: memset_rand() called before OpenSSL init!"));
exit(-1);
}
RAND_bytes((uchar*)data, len);
}

View File

@@ -98,6 +98,12 @@ inline void mylocaltime(struct tm * _Tm, const time_t * _Time) {
#endif
}
class InitOpenSSL {
public:
InitOpenSSL();
~InitOpenSSL();
};
bool checkms(); // returns true if time has changed
uint64 getms(bool checked = false);
@@ -225,51 +231,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);

View File

@@ -11,7 +11,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.8.12</string>
<string>0.8.14</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

Binary file not shown.

View File

@@ -74,6 +74,7 @@
<PrecompiledHeaderOutputFile>$(IntDir)$(TargetName).pch</PrecompiledHeaderOutputFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<Optimization>Disabled</Optimization>
<AdditionalOptions>/Zm110 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -376,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>
@@ -634,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>
@@ -917,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>
@@ -965,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" />
@@ -1354,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" />

View File

@@ -1687,7 +1687,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.8.12;
CURRENT_PROJECT_VERSION = 0.8.14;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -1705,7 +1705,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 0.8.12;
CURRENT_PROJECT_VERSION = 0.8.14;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_OPTIMIZATION_LEVEL = fast;
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
@@ -1731,10 +1731,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.8.12;
CURRENT_PROJECT_VERSION = 0.8.14;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = 0.8;
DYLIB_CURRENT_VERSION = 0.8.12;
DYLIB_CURRENT_VERSION = 0.8.14;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1873,10 +1873,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 0.8.12;
CURRENT_PROJECT_VERSION = 0.8.14;
DEBUG_INFORMATION_FORMAT = dwarf;
DYLIB_COMPATIBILITY_VERSION = 0.8;
DYLIB_CURRENT_VERSION = 0.8.12;
DYLIB_CURRENT_VERSION = 0.8.14;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;

View File

@@ -1,2 +1,2 @@
echo 8012 0.8.12 1
echo 8014 0.8.14 1
# AppVersion AppVersionStr DevChannel