Merge branch 'games' into player

This commit is contained in:
John Preston 2016-09-16 13:44:00 +03:00
commit 37b5329af3
53 changed files with 12563 additions and 9238 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 241 KiB

View file

@ -1866,6 +1866,8 @@ emojiSymbolsActive: sprite(287px, 286px, 21px, 22px);
stickersSettings: sprite(140px, 124px, 21px, 22px);
savedGifsOver: sprite(329px, 286px, 21px, 22px);
savedGifsActive: sprite(350px, 286px, 21px, 22px);
featuredStickersOver: sprite(329px, 264px, 21px, 22px);
featuredStickersActive: sprite(350px, 264px, 21px, 22px);
stickersSettingsUnreadSize: 17px;
stickersSettingsUnreadPosition: point(4px, 5px);

View file

@ -580,6 +580,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_contact" = "a contact information";
"lng_action_pinned_media_location" = "a location mark";
"lng_action_pinned_media_sticker" = "a sticker";
"lng_action_game_score" = "{from} scored {score} in {game}";
"lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached";
"lng_profile_migrate_body" = "To get over this limit, you can upgrade your group to a supergroup.";
@ -683,6 +684,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_switch_stickers" = "Stickers";
"lng_switch_stickers_gifs" = "GIFs & Stickers";
"lng_switch_emoji" = "Emoji";
"lng_stickers_featured_add" = "Add";
"lng_saved_gifs" = "Saved GIFs";
"lng_inline_bot_results" = "Results from {inline_bot}";
@ -762,6 +764,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_open_this_link" = "Open this link?";
"lng_open_link" = "Open";
"lng_allow_bot_pass" = "Do you allow {bot_name} to pass your Telegram name and id to the web pages you open via this bot?";
"lng_allow_bot" = "Allow";
"lng_bot_start" = "Start";
"lng_bot_choose_group" = "Choose Group";
@ -870,6 +874,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_reply_cant" = "Sorry, no way to reply to an old message in supergroup :(";
"lng_reply_cant_forward" = "Sorry, no way to reply to an old message in supergroup :( Do you wish to forward it and add your comment?";
"lng_share_title" = "Share to";
"lng_share_confirm" = "Share";
"lng_share_wrong_user" = "This game was opened from a different user.";
"lng_contact_phone" = "Phone number";
"lng_enter_contact_data" = "New Contact";
"lng_edit_group_title" = "Edit group name";

View file

@ -1857,7 +1857,8 @@ namespace {
photoSizes.push_back(MTP_photoSize(MTP_string("a"), uphoto.vphoto_small, MTP_int(160), MTP_int(160), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("c"), uphoto.vphoto_big, MTP_int(640), MTP_int(640), MTP_int(0)));
return MTP_photo(uphoto.vphoto_id, MTP_long(0), date, MTP_vector<MTPPhotoSize>(photoSizes));
MTPDphoto::Flags photoFlags = 0;
return MTP_photo(MTP_flags(photoFlags), uphoto.vphoto_id, MTP_long(0), date, MTP_vector<MTPPhotoSize>(photoSizes));
}
return MTP_photoEmpty(MTP_long(0));
}

View file

@ -877,7 +877,7 @@ bool AppClass::peerPhotoFail(PeerId peer, const RPCError &error) {
void AppClass::peerClearPhoto(PeerId id) {
if (MTP::authedId() && peerToUser(id) == MTP::authedId()) {
MTP::send(MTPphotos_UpdateProfilePhoto(MTP_inputPhotoEmpty(), MTP_inputPhotoCropAuto()), rpcDone(&AppClass::selfPhotoCleared), rpcFail(&AppClass::peerPhotoFail, id));
MTP::send(MTPphotos_UpdateProfilePhoto(MTP_inputPhotoEmpty()), rpcDone(&AppClass::selfPhotoCleared), rpcFail(&AppClass::peerPhotoFail, id));
} else if (peerIsChat(id)) {
MTP::send(MTPmessages_EditChatPhoto(peerToBareMTPInt(id), MTP_inputChatPhotoEmpty()), rpcDone(&AppClass::chatPhotoCleared, id), rpcFail(&AppClass::peerPhotoFail, id));
} else if (peerIsChannel(id)) {
@ -966,17 +966,17 @@ void AppClass::killDownloadSessions() {
void AppClass::photoUpdated(const FullMsgId &msgId, bool silent, const MTPInputFile &file) {
if (!App::self()) return;
QMap<FullMsgId, PeerId>::iterator i = photoUpdates.find(msgId);
auto i = photoUpdates.find(msgId);
if (i != photoUpdates.end()) {
PeerId id = i.value();
auto id = i.value();
if (MTP::authedId() && peerToUser(id) == MTP::authedId()) {
MTP::send(MTPphotos_UploadProfilePhoto(file, MTP_string(""), MTP_inputGeoPointEmpty(), MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100))), rpcDone(&AppClass::selfPhotoDone), rpcFail(&AppClass::peerPhotoFail, id));
MTP::send(MTPphotos_UploadProfilePhoto(file), rpcDone(&AppClass::selfPhotoDone), rpcFail(&AppClass::peerPhotoFail, id));
} else if (peerIsChat(id)) {
History *hist = App::history(id);
hist->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(hist->peer->asChat()->inputChat, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, hist->sendRequestId);
auto history = App::history(id);
history->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(history->peer->asChat()->inputChat, MTP_inputChatUploadedPhoto(file)), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, history->sendRequestId);
} else if (peerIsChannel(id)) {
History *hist = App::history(id);
hist->sendRequestId = MTP::send(MTPchannels_EditPhoto(hist->peer->asChannel()->inputChannel, MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, hist->sendRequestId);
auto history = App::history(id);
history->sendRequestId = MTP::send(MTPchannels_EditPhoto(history->peer->asChannel()->inputChannel, MTP_inputChatUploadedPhoto(file)), rpcDone(&AppClass::chatPhotoDone, id), rpcFail(&AppClass::peerPhotoFail, id), 0, 0, history->sendRequestId);
}
}
}
@ -1053,7 +1053,8 @@ void AppClass::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId) {
PhotoId id = rand_value<PhotoId>();
MTPPhoto photo(MTP_photo(MTP_long(id), MTP_long(0), MTP_int(unixtime()), MTP_vector<MTPPhotoSize>(photoSizes)));
MTPDphoto::Flags photoFlags = 0;
auto photo = MTP_photo(MTP_flags(photoFlags), MTP_long(id), MTP_long(0), MTP_int(unixtime()), MTP_vector<MTPPhotoSize>(photoSizes));
QString file, filename;
int32 filesize = 0;
@ -1090,10 +1091,9 @@ void AppClass::checkMapVersion() {
AppClass::~AppClass() {
Shortcuts::finish();
if (auto w = _window) {
_window = 0;
delete w;
}
auto window = createAndSwap(_window);
delete window;
anim::stopManager();
stopWebLoadManager();
@ -1102,7 +1102,7 @@ AppClass::~AppClass() {
MTP::finish();
AppObject = 0;
AppObject = nullptr;
deleteAndMark(_uploader);
deleteAndMark(_translator);

View file

@ -193,28 +193,32 @@ void AbstractBox::raiseShadow() {
}
}
ScrollableBox::ScrollableBox(const style::flatScroll &scroll, int32 w) : AbstractBox(w),
_scroll(this, scroll), _innerPtr(0), _topSkip(st::boxTitleHeight), _bottomSkip(st::boxScrollSkip) {
ScrollableBox::ScrollableBox(const style::flatScroll &scroll, int32 w) : AbstractBox(w)
, _scroll(this, scroll)
, _topSkip(st::boxTitleHeight)
, _bottomSkip(st::boxScrollSkip) {
setBlueTitle(true);
}
void ScrollableBox::resizeEvent(QResizeEvent *e) {
_scroll.setGeometry(0, _topSkip, width(), height() - _topSkip - _bottomSkip);
_scroll->setGeometry(0, _topSkip, width(), height() - _topSkip - _bottomSkip);
AbstractBox::resizeEvent(e);
}
void ScrollableBox::init(QWidget *inner, int32 bottomSkip, int32 topSkip) {
void ScrollableBox::init(QWidget *inner, int bottomSkip, int topSkip) {
_bottomSkip = bottomSkip;
_topSkip = topSkip;
_innerPtr = inner;
_scroll.setWidget(_innerPtr);
_scroll.setFocusPolicy(Qt::NoFocus);
ScrollableBox::resizeEvent(0);
_scroll->setWidget(inner);
_scroll->setFocusPolicy(Qt::NoFocus);
ScrollableBox::resizeEvent(nullptr);
}
void ScrollableBox::showAll() {
_scroll.show();
AbstractBox::showAll();
void ScrollableBox::initOwned(QWidget *inner, int bottomSkip, int topSkip) {
_bottomSkip = bottomSkip;
_topSkip = topSkip;
_scroll->setOwnedWidget(inner);
_scroll->setFocusPolicy(Qt::NoFocus);
ScrollableBox::resizeEvent(nullptr);
}
ItemListBox::ItemListBox(const style::flatScroll &scroll, int32 w) : ScrollableBox(scroll, w) {

View file

@ -101,18 +101,20 @@ public:
class ScrollableBox : public AbstractBox {
public:
ScrollableBox(const style::flatScroll &scroll, int32 w = st::boxWideWidth);
void resizeEvent(QResizeEvent *e) override;
ScrollableBox(const style::flatScroll &scroll, int w = st::boxWideWidth);
protected:
void init(QWidget *inner, int32 bottomSkip = st::boxScrollSkip, int32 topSkip = st::boxTitleHeight);
void init(QWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
void initOwned(QWidget *inner, int bottomSkip = st::boxScrollSkip, int topSkip = st::boxTitleHeight);
void showAll() override;
void resizeEvent(QResizeEvent *e) override;
ScrollArea _scroll;
ScrollArea *scrollArea() {
return _scroll;
}
private:
QWidget *_innerPtr;
ChildWidget<ScrollArea> _scroll;
int32 _topSkip, _bottomSkip;
};

View file

@ -49,25 +49,6 @@ confirmInviteUserName: flatLabel(labelDefFlat) {
}
confirmInviteUserNameTop: 227px;
stickersAddIcon: icon {
{ "stickers_add", #ffffff },
};
stickersAddSize: size(30px, 24px);
stickersFeaturedHeight: 32px;
stickersFeaturedFont: contactsNameFont;
stickersFeaturedPosition: point(16px, 6px);
stickersFeaturedBadgeFont: semiboldFont;
stickersFeaturedBadgeSize: 21px;
stickersFeaturedPen: contactsNewItemFg;
stickersFeaturedUnreadBg: msgFileInBg;
stickersFeaturedUnreadSize: 5px;
stickersFeaturedUnreadSkip: 5px;
stickersFeaturedUnreadTop: 7px;
stickersFeaturedInstalled: icon {
{ "mediaview_save_check", #40ace3 }
};
confirmPhoneAboutLabel: flatLabel(labelDefFlat) {
width: 282px;
}
@ -87,3 +68,26 @@ aboutRevokePublicLabel: flatLabel(labelDefFlat) {
}
localStorageBoxSkip: 10px;
shareRowsTop: 12px;
shareRowHeight: 108px;
sharePhotoRadius: 28px;
sharePhotoSmallRadius: 24px;
sharePhotoTop: 6px;
shareSelectWidth: 2px;
shareSelectFg: windowActiveBg;
shareCheckBorder: windowBg;
shareCheckBg: windowActiveBg;
shareCheckRadius: 10px;
shareCheckSmallRadius: 3px;
shareCheckIcon: icon {
{ "default_checkbox_check", windowBg, point(3px, 6px) },
};
shareNameFont: font(11px);
shareNameFg: windowTextFg;
shareNameActiveFg: btnYesColor;
shareNameTop: 6px;
shareColumnSkip: 6px;
shareSelectDuration: 150;
shareActivateDuration: 150;
shareScrollDuration: 300;

View file

@ -19,15 +19,16 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "lang.h"
#include "boxes/confirmbox.h"
#include "confirmbox.h"
#include "lang.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "application.h"
#include "core/click_handler_types.h"
#include "styles/style_boxes.h"
#include "localstorage.h"
TextParseOptions _confirmBoxTextOptions = {
TextParseLinks | TextParseMultiline | TextParseRichText, // flags
@ -195,6 +196,18 @@ void ConfirmLinkBox::onOpenLink() {
UrlClickHandler::doOpen(_url);
}
ConfirmBotGameBox::ConfirmBotGameBox(UserData *bot, const QString &url) : ConfirmBox(lng_allow_bot_pass(lt_bot_name, bot->name), lang(lng_allow_bot))
, _bot(bot)
, _url(url) {
connect(this, SIGNAL(confirmed()), this, SLOT(onOpenLink()));
}
void ConfirmBotGameBox::onOpenLink() {
Ui::hideLayer();
Local::makeBotTrusted(_bot);
UrlClickHandler::doOpen(_url);
}
MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth)
, _close(this, lang(lng_box_ok), st::defaultBoxButton)
, _text(st::boxTextFont, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right())

View file

@ -125,6 +125,21 @@ private:
};
class ConfirmBotGameBox : public ConfirmBox {
Q_OBJECT
public:
ConfirmBotGameBox(UserData *bot, const QString &url);
public slots:
void onOpenLink();
private:
UserData *_bot;
QString _url;
};
class MaxInviteBox : public AbstractBox {
Q_OBJECT

View file

@ -24,7 +24,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "dialogs/dialogs_indexed_list.h"
#include "lang.h"
#include "boxes/addcontactbox.h"
#include "boxes/contactsbox.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "application.h"
@ -1353,11 +1352,11 @@ void ContactsBox::init() {
_cancel.hide();
}
connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
connect(&_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
connect(&_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel()));
connect(&_inner, SIGNAL(mustScrollTo(int, int)), &_scroll, SLOT(scrollToY(int, int)));
connect(&_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int)));
connect(&_inner, SIGNAL(selectAllQuery()), &_filter, SLOT(selectAll()));
connect(&_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername()));
connect(&_inner, SIGNAL(adminAdded()), this, SIGNAL(adminAdded()));
@ -1478,9 +1477,9 @@ void ContactsBox::keyPressEvent(QKeyEvent *e) {
} else if (e->key() == Qt::Key_Up) {
_inner.selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown) {
_inner.selectSkipPage(_scroll.height(), 1);
_inner.selectSkipPage(scrollArea()->height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
_inner.selectSkipPage(_scroll.height(), -1);
_inner.selectSkipPage(scrollArea()->height(), -1);
} else {
ItemListBox::keyPressEvent(e);
}
@ -1530,7 +1529,7 @@ void ContactsBox::onFilterCancel() {
}
void ContactsBox::onFilterUpdate() {
_scroll.scrollToY(0);
scrollArea()->scrollToY(0);
if (_filter.getLastText().isEmpty()) {
_filterCancel.hide();
} else {
@ -1681,7 +1680,7 @@ bool ContactsBox::editAdminFail(const RPCError &error) {
}
void ContactsBox::onScroll() {
_inner.loadProfilePhotos(_scroll.scrollTop());
_inner.loadProfilePhotos(scrollArea()->scrollTop());
}
void ContactsBox::creationDone(const MTPUpdates &updates) {
@ -2245,8 +2244,8 @@ MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox
connect(&_inner, SIGNAL(addRequested()), this, SLOT(onAdd()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&_inner, SIGNAL(mustScrollTo(int, int)), &_scroll, SLOT(scrollToY(int, int)));
connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int)));
connect(&_inner, SIGNAL(loaded()), this, SLOT(onLoaded()));
connect(&_loadTimer, SIGNAL(timeout()), &_inner, SLOT(load()));
@ -2260,9 +2259,9 @@ void MembersBox::keyPressEvent(QKeyEvent *e) {
} else if (e->key() == Qt::Key_Up) {
_inner.selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown) {
_inner.selectSkipPage(_scroll.height(), 1);
_inner.selectSkipPage(scrollArea()->height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
_inner.selectSkipPage(_scroll.height(), -1);
_inner.selectSkipPage(scrollArea()->height(), -1);
} else {
ItemListBox::keyPressEvent(e);
}
@ -2282,7 +2281,7 @@ void MembersBox::resizeEvent(QResizeEvent *e) {
}
void MembersBox::onScroll() {
_inner.loadProfilePhotos(_scroll.scrollTop());
_inner.loadProfilePhotos(scrollArea()->scrollTop());
}
void MembersBox::onAdd() {

View file

@ -252,10 +252,10 @@ void SessionsBox::resizeEvent(QResizeEvent *e) {
void SessionsBox::showAll() {
_done.show();
if (_loading) {
_scroll.hide();
scrollArea()->hide();
_shadow.hide();
} else {
_scroll.show();
scrollArea()->show();
_shadow.show();
}
ScrollableBox::showAll();

View file

@ -0,0 +1,995 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "boxes/sharebox.h"
#include "dialogs/dialogs_indexed_list.h"
#include "styles/style_boxes.h"
#include "observer_peer.h"
#include "lang.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "core/qthelp_url.h"
#include "localstorage.h"
#include "boxes/confirmbox.h"
#include "apiwrap.h"
ShareBox::ShareBox(SubmitCallback &&callback) : ItemListBox(st::boxScroll)
, _callback(std_::move(callback))
, _inner(this)
, _filter(this, st::boxSearchField, lang(lng_participant_filter))
, _filterCancel(this, st::boxSearchCancel)
, _share(this, lang(lng_share_confirm), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, _topShadow(this)
, _bottomShadow(this) {
int topSkip = st::boxTitleHeight + _filter->height();
int bottomSkip = st::boxButtonPadding.top() + _share->height() + st::boxButtonPadding.bottom();
init(_inner, bottomSkip, topSkip);
connect(_inner, SIGNAL(selectedChanged()), this, SLOT(onSelectedChanged()));
connect(_inner, SIGNAL(mustScrollTo(int,int)), this, SLOT(onMustScrollTo(int,int)));
connect(_share, SIGNAL(clicked()), this, SLOT(onShare()));
connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
connect(_filter, SIGNAL(submitted(bool)), _inner, SLOT(onSelectActive()));
connect(_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel()));
connect(_inner, SIGNAL(filterCancel()), this, SLOT(onFilterCancel()));
connect(_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername()));
_filterCancel->setAttribute(Qt::WA_OpaquePaintEvent);
_searchTimer.setSingleShot(true);
connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername()));
prepare();
}
bool ShareBox::onSearchByUsername(bool searchCache) {
auto query = _filter->getLastText().trimmed();
if (query.isEmpty()) {
if (_peopleRequest) {
_peopleRequest = 0;
}
return true;
}
if (query.size() >= MinUsernameLength) {
if (searchCache) {
auto i = _peopleCache.constFind(query);
if (i != _peopleCache.cend()) {
_peopleQuery = query;
_peopleRequest = 0;
peopleReceived(i.value(), 0);
return true;
}
} else if (_peopleQuery != query) {
_peopleQuery = query;
_peopleFull = false;
_peopleRequest = MTP::send(MTPcontacts_Search(MTP_string(_peopleQuery), MTP_int(SearchPeopleLimit)), rpcDone(&ShareBox::peopleReceived), rpcFail(&ShareBox::peopleFailed));
_peopleQueries.insert(_peopleRequest, _peopleQuery);
}
}
return false;
}
void ShareBox::onNeedSearchByUsername() {
if (!onSearchByUsername(true)) {
_searchTimer.start(AutoSearchTimeout);
}
}
void ShareBox::peopleReceived(const MTPcontacts_Found &result, mtpRequestId requestId) {
auto query = _peopleQuery;
auto i = _peopleQueries.find(requestId);
if (i != _peopleQueries.cend()) {
query = i.value();
_peopleCache[query] = result;
_peopleQueries.erase(i);
}
if (_peopleRequest == requestId) {
switch (result.type()) {
case mtpc_contacts_found: {
auto &found = result.c_contacts_found();
App::feedUsers(found.vusers);
App::feedChats(found.vchats);
_inner->peopleReceived(query, found.vresults.c_vector().v);
} break;
}
_peopleRequest = 0;
onScroll();
}
}
bool ShareBox::peopleFailed(const RPCError &error, mtpRequestId requestId) {
if (MTP::isDefaultHandledError(error)) return false;
if (_peopleRequest == requestId) {
_peopleRequest = 0;
_peopleFull = true;
}
return true;
}
void ShareBox::doSetInnerFocus() {
_filter->setFocus();
}
void ShareBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (paint(p)) return;
paintTitle(p, lang(lng_share_title));
}
void ShareBox::resizeEvent(QResizeEvent *e) {
ItemListBox::resizeEvent(e);
_filter->resize(width(), _filter->height());
_filter->moveToLeft(0, st::boxTitleHeight);
_filterCancel->moveToRight(0, st::boxTitleHeight);
_inner->resizeToWidth(width());
moveButtons();
_topShadow->setGeometry(0, st::boxTitleHeight + _filter->height(), width(), st::lineWidth);
_bottomShadow->setGeometry(0, height() - st::boxButtonPadding.bottom() - _share->height() - st::boxButtonPadding.top() - st::lineWidth, width(), st::lineWidth);
}
void ShareBox::keyPressEvent(QKeyEvent *e) {
if (_filter->hasFocus()) {
if (e->key() == Qt::Key_Up) {
_inner->activateSkipColumn(-1);
} else if (e->key() == Qt::Key_Down) {
_inner->activateSkipColumn(1);
} else if (e->key() == Qt::Key_PageUp) {
_inner->activateSkipPage(scrollArea()->height(), -1);
} else if (e->key() == Qt::Key_PageDown) {
_inner->activateSkipPage(scrollArea()->height(), 1);
} else {
ItemListBox::keyPressEvent(e);
}
} else {
ItemListBox::keyPressEvent(e);
}
}
void ShareBox::moveButtons() {
_share->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _share->height());
auto cancelRight = st::boxButtonPadding.right();
if (_inner->hasSelected()) {
cancelRight += _share->width() + st::boxButtonPadding.left();
}
_cancel->moveToRight(cancelRight, _share->y());
}
void ShareBox::onFilterCancel() {
_filter->setText(QString());
}
void ShareBox::onFilterUpdate() {
_filterCancel->setVisible(!_filter->getLastText().isEmpty());
_inner->updateFilter(_filter->getLastText());
}
void ShareBox::onShare() {
if (_callback) {
_callback(_inner->selected());
}
}
void ShareBox::onSelectedChanged() {
_share->setVisible(_inner->hasSelected());
moveButtons();
update();
}
void ShareBox::onMustScrollTo(int top, int bottom) {
auto scrollTop = scrollArea()->scrollTop(), scrollBottom = scrollTop + scrollArea()->height();
auto from = scrollTop, to = scrollTop;
if (scrollTop > top) {
to = top;
} else if (scrollBottom < bottom) {
to = bottom - (scrollBottom - scrollTop);
}
if (from != to) {
START_ANIMATION(_scrollAnimation, func([this]() {
scrollArea()->scrollToY(_scrollAnimation.current(scrollArea()->scrollTop()));
}), from, to, st::shareScrollDuration, anim::sineInOut);
}
}
void ShareBox::onScroll() {
auto scroll = scrollArea();
auto scrollTop = scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + scroll->height());
}
namespace internal {
ShareInner::ShareInner(QWidget *parent) : ScrolledWidget(parent)
, _chatsIndexed(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) {
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
_rowsTop = st::shareRowsTop;
_rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent);
auto dialogs = App::main()->dialogsList();
for_const (auto row, dialogs->all()) {
auto history = row->history();
if (history->peer->canWrite()) {
_chatsIndexed->addToEnd(history);
}
}
_filter = qsl("a");
updateFilter();
prepareWideCheckIcons();
using UpdateFlag = Notify::PeerUpdate::Flag;
auto observeEvents = UpdateFlag::NameChanged | UpdateFlag::PhotoChanged;
Notify::registerPeerObserver(observeEvents, this, &ShareInner::notifyPeerUpdated);
}
void ShareInner::setVisibleTopBottom(int visibleTop, int visibleBottom) {
loadProfilePhotos(visibleTop);
}
void ShareInner::activateSkipRow(int direction) {
activateSkipColumn(direction * _columnCount);
}
int ShareInner::displayedChatsCount() const {
return _filter.isEmpty() ? _chatsIndexed->size() : (_filtered.size() + d_byUsernameFiltered.size());
}
void ShareInner::activateSkipColumn(int direction) {
if (_active < 0) {
if (direction > 0) {
setActive(0);
}
return;
}
auto count = displayedChatsCount();
auto active = _active + direction;
if (active < 0) {
active = (_active > 0) ? 0 : -1;
}
if (active >= count) {
active = count - 1;
}
setActive(active);
}
void ShareInner::activateSkipPage(int pageHeight, int direction) {
activateSkipRow(direction * (pageHeight / _rowHeight));
}
void ShareInner::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.flags & Notify::PeerUpdate::Flag::NameChanged) {
_chatsIndexed->peerNameChanged(update.peer, update.oldNames, update.oldNameFirstChars);
}
updateChat(update.peer);
}
void ShareInner::updateChat(PeerData *peer) {
auto i = _dataMap.find(peer);
if (i != _dataMap.cend()) {
updateChatName(i.value(), peer);
repaintChat(peer);
}
}
void ShareInner::updateChatName(Chat *chat, PeerData *peer) {
chat->name.setText(st::shareNameFont, peer->name, _textNameOptions);
}
void ShareInner::repaintChatAtIndex(int index) {
if (index < 0) return;
auto row = index / _columnCount;
auto column = index % _columnCount;
update(rtlrect(_rowsLeft + qFloor(column * _rowWidthReal), row * _rowHeight, _rowWidth, _rowHeight, width()));
}
ShareInner::Chat *ShareInner::getChatAtIndex(int index) {
if (index < 0) return nullptr;
auto row = ([this, index]() -> Dialogs::Row* {
if (_filter.isEmpty()) return _chatsIndexed->rowAtY(index, 1);
return (index < _filtered.size()) ? _filtered[index] : nullptr;
})();
if (row) {
return static_cast<Chat*>(row->attached);
}
if (!_filter.isEmpty()) {
index -= _filtered.size();
if (index >= 0 && index < d_byUsernameFiltered.size()) {
return d_byUsernameFiltered[index];
}
}
return nullptr;
}
void ShareInner::repaintChat(PeerData *peer) {
repaintChatAtIndex(chatIndex(peer));
}
int ShareInner::chatIndex(PeerData *peer) const {
int index = 0;
if (_filter.isEmpty()) {
for_const (auto row, _chatsIndexed->all()) {
if (row->history()->peer == peer) {
return index;
}
++index;
}
} else {
for_const (auto row, _filtered) {
if (row->history()->peer == peer) {
return index;
}
++index;
}
for_const (auto row, d_byUsernameFiltered) {
if (row->peer == peer) {
return index;
}
++index;
}
}
return -1;
}
void ShareInner::loadProfilePhotos(int yFrom) {
if (yFrom < 0) {
yFrom = 0;
}
if (auto part = (yFrom % _rowHeight)) {
yFrom -= part;
}
int yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5 * _columnCount;
if (!yTo) {
return;
}
yFrom *= _columnCount;
yTo *= _columnCount;
MTP::clearLoaderPriorities();
if (_filter.isEmpty()) {
if (!_chatsIndexed->isEmpty()) {
auto i = _chatsIndexed->cfind(yFrom, _rowHeight);
for (auto end = _chatsIndexed->cend(); i != end; ++i) {
if (((*i)->pos() * _rowHeight) >= yTo) {
break;
}
(*i)->history()->peer->loadUserpic();
}
}
} else if (!_filtered.isEmpty()) {
int from = yFrom / _rowHeight;
if (from < 0) from = 0;
if (from < _filtered.size()) {
int to = (yTo / _rowHeight) + 1;
if (to > _filtered.size()) to = _filtered.size();
for (; from < to; ++from) {
_filtered[from]->history()->peer->loadUserpic();
}
}
}
}
ShareInner::Chat *ShareInner::getChat(Dialogs::Row *row) {
auto data = static_cast<Chat*>(row->attached);
if (!data) {
auto peer = row->history()->peer;
auto i = _dataMap.constFind(peer);
if (i == _dataMap.cend()) {
_dataMap.insert(peer, data = new Chat(peer));
updateChatName(data, peer);
} else {
data = i.value();
}
row->attached = data;
}
return data;
}
void ShareInner::setActive(int active) {
if (active != _active) {
auto changeNameFg = [this](int index, style::color from, style::color to) {
if (auto chat = getChatAtIndex(index)) {
START_ANIMATION(chat->nameFg, func([this, chat] {
repaintChat(chat->peer);
}), from->c, to->c, st::shareActivateDuration, anim::linear);
}
};
changeNameFg(_active, st::shareNameActiveFg, st::shareNameFg);
_active = active;
changeNameFg(_active, st::shareNameFg, st::shareNameActiveFg);
}
auto y = (_active < _columnCount) ? 0 : (_rowsTop + ((_active / _columnCount) * _rowHeight));
emit mustScrollTo(y, y + _rowHeight);
}
void ShareInner::paintChat(Painter &p, Chat *chat, int index) {
auto x = _rowsLeft + qFloor((index % _columnCount) * _rowWidthReal);
auto y = _rowsTop + (index / _columnCount) * _rowHeight;
auto selectionLevel = chat->selection.current(chat->selected ? 1. : 0.);
auto w = width();
auto photoLeft = (_rowWidth - (st::sharePhotoRadius * 2)) / 2;
auto photoTop = st::sharePhotoTop;
if (chat->selection.isNull()) {
if (!chat->wideUserpicCache.isNull()) {
chat->wideUserpicCache = QPixmap();
}
auto userpicRadius = chat->selected ? st::sharePhotoSmallRadius : st::sharePhotoRadius;
auto userpicShift = st::sharePhotoRadius - userpicRadius;
auto userpicLeft = x + photoLeft + userpicShift;
auto userpicTop = y + photoTop + userpicShift;
chat->peer->paintUserpicLeft(p, userpicRadius * 2, userpicLeft, userpicTop, w);
} else {
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
auto userpicRadius = qRound(WideCacheScale * (st::sharePhotoRadius + (st::sharePhotoSmallRadius - st::sharePhotoRadius) * selectionLevel));
auto userpicShift = WideCacheScale * st::sharePhotoRadius - userpicRadius;
auto userpicLeft = x + photoLeft - (WideCacheScale - 1) * st::sharePhotoRadius + userpicShift;
auto userpicTop = y + photoTop - (WideCacheScale - 1) * st::sharePhotoRadius + userpicShift;
auto to = QRect(userpicLeft, userpicTop, userpicRadius * 2, userpicRadius * 2);
auto from = QRect(QPoint(0, 0), chat->wideUserpicCache.size());
p.drawPixmapLeft(to, w, chat->wideUserpicCache, from);
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
}
if (selectionLevel > 0) {
p.setRenderHint(QPainter::HighQualityAntialiasing, true);
p.setOpacity(snap(selectionLevel, 0., 1.));
p.setBrush(Qt::NoBrush);
QPen pen = st::shareSelectFg;
pen.setWidth(st::shareSelectWidth);
p.setPen(pen);
p.drawEllipse(myrtlrect(x + photoLeft, y + photoTop, st::sharePhotoRadius * 2, st::sharePhotoRadius * 2));
p.setOpacity(1.);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
}
removeFadeOutedIcons(chat);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
for (auto &icon : chat->icons) {
auto fadeIn = icon.fadeIn.current(1.);
auto fadeOut = icon.fadeOut.current(1.);
auto iconRadius = qRound(WideCacheScale * (st::shareCheckSmallRadius + fadeOut * (st::shareCheckRadius - st::shareCheckSmallRadius)));
auto iconShift = WideCacheScale * st::shareCheckRadius - iconRadius;
auto iconLeft = x + photoLeft + 2 * st::sharePhotoRadius + st::shareSelectWidth - 2 * st::shareCheckRadius - (WideCacheScale - 1) * st::shareCheckRadius + iconShift;
auto iconTop = y + photoTop + 2 * st::sharePhotoRadius + st::shareSelectWidth - 2 * st::shareCheckRadius - (WideCacheScale - 1) * st::shareCheckRadius + iconShift;
auto to = QRect(iconLeft, iconTop, iconRadius * 2, iconRadius * 2);
auto from = QRect(QPoint(0, 0), _wideCheckIconCache.size());
auto opacity = fadeIn * fadeOut;
p.setOpacity(opacity);
if (fadeOut < 1.) {
p.drawPixmapLeft(to, w, icon.wideCheckCache, from);
} else {
auto divider = qRound((WideCacheScale - 2) * st::shareCheckRadius + fadeIn * 3 * st::shareCheckRadius);
p.drawPixmapLeft(QRect(iconLeft, iconTop, divider, iconRadius * 2), w, _wideCheckIconCache, QRect(0, 0, divider * cIntRetinaFactor(), _wideCheckIconCache.height()));
p.drawPixmapLeft(QRect(iconLeft + divider, iconTop, iconRadius * 2 - divider, iconRadius * 2), w, _wideCheckCache, QRect(divider * cIntRetinaFactor(), 0, _wideCheckCache.width() - divider * cIntRetinaFactor(), _wideCheckCache.height()));
}
}
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
p.setOpacity(1.);
if (chat->nameFg.isNull()) {
p.setPen((index == _active) ? st::shareNameActiveFg : st::shareNameFg);
} else {
p.setPen(chat->nameFg.current());
}
auto nameWidth = (_rowWidth - st::shareColumnSkip);
auto nameLeft = st::shareColumnSkip / 2;
auto nameTop = photoTop + st::sharePhotoRadius * 2 + st::shareNameTop;
chat->name.drawLeftElided(p, x + nameLeft, y + nameTop, nameWidth, w, 2, style::al_top, 0, -1, 0, true);
}
ShareInner::Chat::Chat(PeerData *peer) : peer(peer), name(st::sharePhotoRadius * 2) {
}
void ShareInner::paintEvent(QPaintEvent *e) {
Painter p(this);
auto r = e->rect();
p.setClipRect(r);
p.fillRect(r, st::white);
auto yFrom = r.y(), yTo = r.y() + r.height();
auto rowFrom = yFrom / _rowHeight;
auto rowTo = (yTo + _rowHeight - 1) / _rowHeight;
auto indexFrom = rowFrom * _columnCount;
auto indexTo = rowTo * _columnCount;
if (_filter.isEmpty()) {
if (!_chatsIndexed->isEmpty()) {
auto i = _chatsIndexed->cfind(indexFrom, 1);
for (auto end = _chatsIndexed->cend(); i != end; ++i) {
if (indexFrom >= indexTo) {
break;
}
paintChat(p, getChat(*i), indexFrom);
++indexFrom;
}
} else {
// empty
p.setFont(st::noContactsFont);
p.setPen(st::noContactsColor);
}
} else {
if (_filtered.isEmpty() && _byUsernameFiltered.isEmpty()) {
// empty
p.setFont(st::noContactsFont);
p.setPen(st::noContactsColor);
} else {
auto filteredSize = _filtered.size();
if (filteredSize) {
if (indexFrom < 0) indexFrom = 0;
while (indexFrom < indexTo) {
if (indexFrom >= _filtered.size()) {
break;
}
paintChat(p, getChat(_filtered[indexFrom]), indexFrom);
++indexFrom;
}
indexFrom -= filteredSize;
indexTo -= filteredSize;
}
if (!_byUsernameFiltered.isEmpty()) {
if (indexFrom < 0) indexFrom = 0;
while (indexFrom < indexTo) {
if (indexFrom >= d_byUsernameFiltered.size()) {
break;
}
paintChat(p, d_byUsernameFiltered[indexFrom], filteredSize + indexFrom);
++indexFrom;
}
}
}
}
}
void ShareInner::enterEvent(QEvent *e) {
setMouseTracking(true);
}
void ShareInner::leaveEvent(QEvent *e) {
setMouseTracking(false);
}
void ShareInner::mouseMoveEvent(QMouseEvent *e) {
updateUpon(e->pos());
setCursor((_upon >= 0) ? style::cur_pointer : style::cur_default);
}
void ShareInner::updateUpon(const QPoint &pos) {
auto x = pos.x(), y = pos.y();
auto row = (y - _rowsTop) / _rowHeight;
auto column = qFloor((x - _rowsLeft) / _rowWidthReal);
auto left = _rowsLeft + qFloor(column * _rowWidthReal) + st::shareColumnSkip / 2;
auto top = _rowsTop + row * _rowHeight + st::sharePhotoTop;
auto xupon = (x >= left) && (x < left + (_rowWidth - st::shareColumnSkip));
auto yupon = (y >= top) && (y < top + st::sharePhotoRadius * 2 + st::shareNameTop + st::shareNameFont->height * 2);
auto upon = (xupon && yupon) ? (row * _columnCount + column) : -1;
if (upon >= displayedChatsCount()) {
upon = -1;
}
_upon = upon;
}
void ShareInner::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
updateUpon(e->pos());
changeCheckState(getChatAtIndex(_upon));
}
}
void ShareInner::onSelectActive() {
changeCheckState(getChatAtIndex(_active > 0 ? _active : 0));
}
void ShareInner::resizeEvent(QResizeEvent *e) {
_columnSkip = (width() - _columnCount * st::sharePhotoRadius * 2) / float64(_columnCount + 1);
_rowWidthReal = st::sharePhotoRadius * 2 + _columnSkip;
_rowsLeft = qFloor(_columnSkip / 2);
_rowWidth = qFloor(_rowWidthReal);
update();
}
struct AnimBumpy {
AnimBumpy(float64 bump) : bump(bump)
, dt0(bump - sqrt(bump * (bump - 1.)))
, k(1 / (2 * dt0 - 1)) {
}
float64 bump;
float64 dt0;
float64 k;
};
float64 anim_bumpy(const float64 &delta, const float64 &dt) {
static AnimBumpy data = { 1.25 };
return delta * (data.bump - data.k * (dt - data.dt0) * (dt - data.dt0));
}
void ShareInner::changeCheckState(Chat *chat) {
if (!chat) return;
if (!_filter.isEmpty()) {
auto row = _chatsIndexed->getRow(chat->peer->id);
if (!row) {
row = _chatsIndexed->addToEnd(App::history(chat->peer)).value(0);
}
chat = getChat(row);
if (!chat->selected) {
_chatsIndexed->moveToTop(chat->peer);
}
emit filterCancel();
}
chat->selected = !chat->selected;
if (chat->selected) {
_selected.insert(chat->peer);
chat->icons.push_back(Chat::Icon());
START_ANIMATION(chat->icons.back().fadeIn, func([this, chat] {
repaintChat(chat->peer);
}), 0, 1, st::shareSelectDuration, anim::linear);
} else {
_selected.remove(chat->peer);
prepareWideCheckIconCache(&chat->icons.back());
START_ANIMATION(chat->icons.back().fadeOut, func([this, chat] {
removeFadeOutedIcons(chat);
repaintChat(chat->peer);
}), 1, 0, st::shareSelectDuration, anim::linear);
}
prepareWideUserpicCache(chat);
START_ANIMATION(chat->selection, func([this, chat] {
repaintChat(chat->peer);
}), chat->selected ? 0 : 1, chat->selected ? 1 : 0, st::shareSelectDuration, anim_bumpy);
if (chat->selected) {
setActive(chatIndex(chat->peer));
}
emit selectedChanged();
}
void ShareInner::removeFadeOutedIcons(Chat *chat) {
while (!chat->icons.empty() && chat->icons.front().fadeIn.isNull() && chat->icons.front().fadeOut.isNull()) {
if (chat->icons.size() > 1 || !chat->selected) {
chat->icons.pop_front();
} else {
break;
}
}
}
void ShareInner::prepareWideUserpicCache(Chat *chat) {
if (chat->wideUserpicCache.isNull()) {
auto size = st::sharePhotoRadius * 2;
auto wideSize = size * WideCacheScale;
QImage cache(wideSize * cIntRetinaFactor(), wideSize * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor());
{
Painter p(&cache);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(0, 0, wideSize, wideSize, Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
chat->peer->paintUserpic(p, size, (wideSize - size) / 2, (wideSize - size) / 2);
}
chat->wideUserpicCache = App::pixmapFromImageInPlace(std_::move(cache));
chat->wideUserpicCache.setDevicePixelRatio(cRetinaFactor());
}
}
void ShareInner::prepareWideCheckIconCache(Chat::Icon *icon) {
QImage wideCache(_wideCheckCache.width(), _wideCheckCache.height(), QImage::Format_ARGB32_Premultiplied);
wideCache.setDevicePixelRatio(cRetinaFactor());
{
Painter p(&wideCache);
p.setCompositionMode(QPainter::CompositionMode_Source);
auto iconRadius = WideCacheScale * st::shareCheckRadius;
auto divider = qRound((WideCacheScale - 2) * st::shareCheckRadius + icon->fadeIn.current(1.) * 3 * st::shareCheckRadius);
p.drawPixmapLeft(QRect(0, 0, divider, iconRadius * 2), width(), _wideCheckIconCache, QRect(0, 0, divider * cIntRetinaFactor(), _wideCheckIconCache.height()));
p.drawPixmapLeft(QRect(divider, 0, iconRadius * 2 - divider, iconRadius * 2), width(), _wideCheckCache, QRect(divider * cIntRetinaFactor(), 0, _wideCheckCache.width() - divider * cIntRetinaFactor(), _wideCheckCache.height()));
}
icon->wideCheckCache = App::pixmapFromImageInPlace(std_::move(wideCache));
icon->wideCheckCache.setDevicePixelRatio(cRetinaFactor());
}
void ShareInner::prepareWideCheckIcons() {
auto size = st::shareCheckRadius * 2;
auto wideSize = size * WideCacheScale;
QImage cache(wideSize * cIntRetinaFactor(), wideSize * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor());
{
Painter p(&cache);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(0, 0, wideSize, wideSize, Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.setRenderHint(QPainter::HighQualityAntialiasing, true);
auto pen = st::shareCheckBorder->p;
pen.setWidth(st::shareSelectWidth);
p.setPen(pen);
p.setBrush(st::shareCheckBg);
auto ellipse = QRect((wideSize - size) / 2, (wideSize - size) / 2, size, size);
p.drawEllipse(ellipse);
}
QImage cacheIcon = cache;
{
Painter p(&cacheIcon);
auto ellipse = QRect((wideSize - size) / 2, (wideSize - size) / 2, size, size);
st::shareCheckIcon.paint(p, ellipse.topLeft(), wideSize);
}
_wideCheckCache = App::pixmapFromImageInPlace(std_::move(cache));
_wideCheckCache.setDevicePixelRatio(cRetinaFactor());
_wideCheckIconCache = App::pixmapFromImageInPlace(std_::move(cacheIcon));
_wideCheckIconCache.setDevicePixelRatio(cRetinaFactor());
}
bool ShareInner::hasSelected() const {
return _selected.size();
}
void ShareInner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
QStringList f;
if (!filter.isEmpty()) {
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
if (_filter != filter) {
_filter = filter;
_byUsernameFiltered.clear();
for (int i = 0, l = d_byUsernameFiltered.size(); i < l; ++i) {
delete d_byUsernameFiltered[i];
}
d_byUsernameFiltered.clear();
if (_filter.isEmpty()) {
refresh();
} else {
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
_filtered.clear();
if (!f.isEmpty()) {
const Dialogs::List *toFilter = nullptr;
if (!_chatsIndexed->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
auto found = _chatsIndexed->filtered(fi->at(0));
if (found->isEmpty()) {
toFilter = nullptr;
break;
}
if (!toFilter || toFilter->size() > found->size()) {
toFilter = found;
}
}
}
if (toFilter) {
_filtered.reserve(toFilter->size());
for_const (auto row, *toFilter) {
auto &names = row->history()->peer->names;
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
auto filterName = *fi;
for (ni = nb; ni != ne; ++ni) {
if (ni->startsWith(*fi)) {
break;
}
}
if (ni == ne) {
break;
}
}
if (fi == fe) {
_filtered.push_back(row);
}
}
}
}
refresh();
_searching = true;
emit searchByUsername();
}
setActive(-1);
update();
loadProfilePhotos(0);
}
}
void ShareInner::peopleReceived(const QString &query, const QVector<MTPPeer> &people) {
_lastQuery = query.toLower().trimmed();
if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1);
int32 already = _byUsernameFiltered.size();
_byUsernameFiltered.reserve(already + people.size());
d_byUsernameFiltered.reserve(already + people.size());
for_const (auto &mtpPeer, people) {
auto peerId = peerFromMTP(mtpPeer);
int j = 0;
for (; j < already; ++j) {
if (_byUsernameFiltered[j]->id == peerId) break;
}
if (j == already) {
auto *peer = App::peer(peerId);
if (!peer || !peer->canWrite()) continue;
auto chat = new Chat(peer);
updateChatName(chat, peer);
if (auto row = _chatsIndexed->getRow(peer->id)) {
continue;
}
_byUsernameFiltered.push_back(peer);
d_byUsernameFiltered.push_back(chat);
}
}
_searching = false;
refresh();
}
void ShareInner::refresh() {
auto count = displayedChatsCount();
if (count) {
auto rows = (count / _columnCount) + (count % _columnCount ? 1 : 0);
resize(width(), _rowsTop + rows * _rowHeight);
} else {
resize(width(), st::noContactsHeight);
}
update();
}
ShareInner::~ShareInner() {
for_const (auto chat, _dataMap) {
delete chat;
}
}
QVector<PeerData*> ShareInner::selected() const {
QVector<PeerData*> result;
result.reserve(_dataMap.size());
for_const (auto chat, _dataMap) {
if (chat->selected) {
result.push_back(chat->peer);
}
}
return result;
}
} // namespace internal
QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) {
auto shareHashData = QByteArray(0x10, Qt::Uninitialized);
auto ints = reinterpret_cast<int32*>(shareHashData.data());
ints[0] = MTP::authedId();
ints[1] = fullId.channel;
ints[2] = fullId.msg;
ints[3] = 0;
auto key128Size = 0x10;
auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized);
hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data());
if (!Local::encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) {
return url;
}
auto shareHash = shareHashEncrypted.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
auto shareUrl = qsl("tg://share_game_score?hash=") + QString::fromLatin1(shareHash);
auto shareComponent = qsl("tgShareScoreUrl=") + qthelp::url_encode(shareUrl);
auto hashPosition = url.indexOf('#');
if (hashPosition < 0) {
return url + '#' + shareComponent;
}
auto hash = url.mid(hashPosition + 1);
if (hash.indexOf('=') >= 0 || hash.indexOf('?') >= 0) {
return url + '&' + shareComponent;
}
if (!hash.isEmpty()) {
return url + '?' + shareComponent;
}
return url + shareComponent;
}
namespace {
void shareGameScoreFromItem(HistoryItem *item) {
Ui::showLayer(new ShareBox([msgId = item->fullId()](const QVector<PeerData*> &result) {
MTPmessages_ForwardMessages::Flags sendFlags = MTPmessages_ForwardMessages::Flag::f_with_my_score;
MTPVector<MTPint> msgIds = MTP_vector<MTPint>(1, MTP_int(msgId.msg));
if (auto main = App::main()) {
if (auto item = App::histItemById(msgId)) {
for_const (auto peer, result) {
MTPVector<MTPlong> random = MTP_vector<MTPlong>(1, rand_value<MTPlong>());
MTP::send(MTPmessages_ForwardMessages(MTP_flags(sendFlags), item->history()->peer->input, msgIds, random, peer->input), main->rpcDone(&MainWidget::sentUpdatesReceived));
}
}
}
Ui::hideLayer();
}));
}
class GameMessageResolvedCallback : public SharedCallback<void, ChannelData*, MsgId> {
public:
void call(ChannelData *channel, MsgId msgId) const override {
if (auto item = App::histItemById(channel, msgId)) {
shareGameScoreFromItem(item);
} else {
Ui::showLayer(new InformBox(lang(lng_edit_deleted)));
}
}
};
} // namespace
void shareGameScoreByHash(const QString &hash) {
auto key128Size = 0x10;
auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() % 0x10) != 0) {
Ui::showLayer(new InformBox(lang(lng_confirm_phone_link_invalid)));
return;
}
auto hashData = QByteArray(hashEncrypted.size() - key128Size, Qt::Uninitialized);
if (!Local::decrypt(hashEncrypted.constData() + key128Size, hashData.data(), hashEncrypted.size() - key128Size, hashEncrypted.constData())) {
return;
}
char checkSha1[20] = { 0 };
if (memcmp(hashSha1(hashData.constData(), hashData.size(), checkSha1), hashEncrypted.constData(), key128Size) != 0) {
Ui::showLayer(new InformBox(lang(lng_share_wrong_user)));
return;
}
auto ints = reinterpret_cast<int32*>(hashData.data());
if (ints[0] != MTP::authedId()) {
Ui::showLayer(new InformBox(lang(lng_share_wrong_user)));
return;
}
auto channelId = ints[1];
auto msgId = ints[2];
if (auto item = App::histItemById(channelId, msgId)) {
shareGameScoreFromItem(item);
} else if (App::api()) {
auto channel = channelId ? App::channelLoaded(channelId) : nullptr;
if (channel || !channelId) {
App::api()->requestMessageData(channel, msgId, std_::make_unique<GameMessageResolvedCallback>());
}
}
}

View file

@ -0,0 +1,217 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "abstractbox.h"
#include "core/lambda_wrap.h"
#include "core/observer.h"
namespace Dialogs {
class Row;
class IndexedList;
} // namespace Dialogs
namespace internal {
class ShareInner;
} // namespace internal
namespace Notify {
struct PeerUpdate;
} // namespace Notify
QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId);
void shareGameScoreByHash(const QString &hash);
class ShareBox : public ItemListBox, public RPCSender {
Q_OBJECT
public:
using SubmitCallback = base::lambda_unique<void(const QVector<PeerData*> &)>;
ShareBox(SubmitCallback &&callback);
private slots:
void onFilterUpdate();
void onFilterCancel();
void onScroll();
bool onSearchByUsername(bool searchCache = false);
void onNeedSearchByUsername();
void onShare();
void onSelectedChanged();
void onMustScrollTo(int top, int bottom);
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void doSetInnerFocus() override;
private:
void moveButtons();
void peopleReceived(const MTPcontacts_Found &result, mtpRequestId requestId);
bool peopleFailed(const RPCError &error, mtpRequestId requestId);
SubmitCallback _callback;
ChildWidget<internal::ShareInner> _inner;
ChildWidget<InputField> _filter;
ChildWidget<IconedButton> _filterCancel;
ChildWidget<BoxButton> _share;
ChildWidget<BoxButton> _cancel;
ChildWidget<ScrollableBoxShadow> _topShadow;
ChildWidget<ScrollableBoxShadow> _bottomShadow;
QTimer _searchTimer;
QString _peopleQuery;
bool _peopleFull = false;
mtpRequestId _peopleRequest = 0;
using PeopleCache = QMap<QString, MTPcontacts_Found>;
PeopleCache _peopleCache;
using PeopleQueries = QMap<mtpRequestId, QString>;
PeopleQueries _peopleQueries;
IntAnimation _scrollAnimation;
};
namespace internal {
class ShareInner : public ScrolledWidget, public RPCSender, public Notify::Observer {
Q_OBJECT
public:
ShareInner(QWidget *parent);
QVector<PeerData*> selected() const;
bool hasSelected() const;
void peopleReceived(const QString &query, const QVector<MTPPeer> &people);
void activateSkipRow(int direction);
void activateSkipColumn(int direction);
void activateSkipPage(int pageHeight, int direction);
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void updateFilter(QString filter = QString());
~ShareInner();
public slots:
void onSelectActive();
signals:
void mustScrollTo(int ymin, int ymax);
void filterCancel();
void searchByUsername();
void selectedChanged();
protected:
void paintEvent(QPaintEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
int displayedChatsCount() const;
static constexpr int WideCacheScale = 4;
struct Chat {
Chat(PeerData *peer);
PeerData *peer;
Text name;
bool selected = false;
QPixmap wideUserpicCache;
ColorAnimation nameFg;
FloatAnimation selection;
struct Icon {
FloatAnimation fadeIn;
FloatAnimation fadeOut;
QPixmap wideCheckCache;
};
QList<Icon> icons;
};
void paintChat(Painter &p, Chat *chat, int index);
void updateChat(PeerData *peer);
void updateChatName(Chat *chat, PeerData *peer);
void repaintChat(PeerData *peer);
void removeFadeOutedIcons(Chat *chat);
void prepareWideUserpicCache(Chat *chat);
void prepareWideCheckIconCache(Chat::Icon *icon);
void prepareWideCheckIcons();
int chatIndex(PeerData *peer) const;
void repaintChatAtIndex(int index);
Chat *getChatAtIndex(int index);
void loadProfilePhotos(int yFrom);
void changeCheckState(Chat *chat);
Chat *getChat(Dialogs::Row *row);
void setActive(int active);
void updateUpon(const QPoint &pos);
void refresh();
float64 _columnSkip = 0.;
float64 _rowWidthReal = 0.;
int _rowsLeft = 0;
int _rowsTop = 0;
int _rowWidth = 0;
int _rowHeight = 0;
int _columnCount = 4;
int _active = -1;
int _upon = -1;
std_::unique_ptr<Dialogs::IndexedList> _chatsIndexed;
QString _filter;
using FilteredDialogs = QVector<Dialogs::Row*>;
FilteredDialogs _filtered;
QPixmap _wideCheckCache, _wideCheckIconCache;
using DataMap = QMap<PeerData*, Chat*>;
DataMap _dataMap;
using SelectedChats = OrderedSet<PeerData*>;
SelectedChats _selected;
ChatData *data(Dialogs::Row *row);
bool _searching = false;
QString _lastQuery;
using ByUsernameRows = QVector<PeerData*>;
using ByUsernameDatas = QVector<Chat*>;
ByUsernameRows _byUsernameFiltered;
ByUsernameDatas d_byUsernameFiltered;
};
} // namespace internal

View file

@ -24,11 +24,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stickersetbox.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "stickers/stickers.h"
#include "boxes/confirmbox.h"
#include "apiwrap.h"
#include "localstorage.h"
#include "dialogs/dialogs_layout.h"
#include "styles/style_boxes.h"
#include "styles/style_stickers.h"
namespace {
@ -37,40 +39,7 @@ constexpr int kArchivedLimitPerPage = 30;
} // namespace
namespace Stickers {
void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) {
auto &v = d.vsets.c_vector().v;
auto &order = Global::RefStickerSetsOrder();
Stickers::Order archived;
archived.reserve(v.size());
QMap<uint64, uint64> setsToRequest;
for_const (auto &stickerSet, v) {
if (stickerSet.type() == mtpc_stickerSetCovered && stickerSet.c_stickerSetCovered().vset.type() == mtpc_stickerSet) {
auto set = Stickers::feedSet(stickerSet.c_stickerSetCovered().vset.c_stickerSet());
if (set->stickers.isEmpty()) {
setsToRequest.insert(set->id, set->access);
}
auto index = order.indexOf(set->id);
if (index >= 0) {
order.removeAt(index);
}
archived.push_back(set->id);
}
}
if (!setsToRequest.isEmpty()) {
for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
App::api()->scheduleStickerSetRequest(i.key(), i.value());
}
App::api()->requestStickerSets();
}
Local::writeArchivedStickers();
Ui::showLayer(new StickersBox(archived), KeepOtherLayers);
}
} // namespace Stickers
StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : TWidget()
StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : ScrolledWidget()
, _input(set) {
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
switch (set.type()) {
@ -80,6 +49,8 @@ StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : TWidget()
MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&StickerSetInner::gotSet), rpcFail(&StickerSetInner::failedSet));
App::main()->updateStickers();
setMouseTracking(true);
_previewTimer.setSingleShot(true);
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview()));
}
@ -87,15 +58,20 @@ StickerSetInner::StickerSetInner(const MTPInputStickerSet &set) : TWidget()
void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
_pack.clear();
_emoji.clear();
_packOvers.clear();
_selected = -1;
setCursor(style::cur_default);
if (set.type() == mtpc_messages_stickerSet) {
auto &d(set.c_messages_stickerSet());
auto &v(d.vdocuments.c_vector().v);
_pack.reserve(v.size());
_packOvers.reserve(v.size());
for (int i = 0, l = v.size(); i < l; ++i) {
auto doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker()) continue;
_pack.push_back(doc);
_packOvers.push_back(FloatAnimation());
}
auto &packs(d.vpacks.c_vector().v);
for (int i = 0, l = packs.size(); i < l; ++i) {
@ -144,6 +120,8 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) {
}
_loaded = true;
updateSelected();
emit updateButtons();
}
@ -200,12 +178,13 @@ void StickerSetInner::installDone(const MTPmessages_StickerSetInstallResult &res
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive());
} else if (wasArchived) {
} else {
if (wasArchived) {
Local::writeArchivedStickers();
}
Local::writeInstalledStickers();
emit App::main()->stickersUpdated();
}
emit installed(_setId);
}
@ -218,15 +197,16 @@ bool StickerSetInner::installFail(const RPCError &error) {
}
void StickerSetInner::mousePressEvent(QMouseEvent *e) {
int32 index = stickerFromGlobalPos(e->globalPos());
int index = stickerFromGlobalPos(e->globalPos());
if (index >= 0 && index < _pack.size()) {
_previewTimer.start(QApplication::startDragTime());
}
}
void StickerSetInner::mouseMoveEvent(QMouseEvent *e) {
updateSelected();
if (_previewShown >= 0) {
int32 index = stickerFromGlobalPos(e->globalPos());
int index = stickerFromGlobalPos(e->globalPos());
if (index >= 0 && index < _pack.size() && index != _previewShown) {
_previewShown = index;
Ui::showMediaPreview(_pack.at(_previewShown));
@ -235,11 +215,47 @@ void StickerSetInner::mouseMoveEvent(QMouseEvent *e) {
}
void StickerSetInner::mouseReleaseEvent(QMouseEvent *e) {
if (_previewShown >= 0) {
_previewShown = -1;
return;
}
if (_previewTimer.isActive()) {
_previewTimer.stop();
int index = stickerFromGlobalPos(e->globalPos());
if (index >= 0 && index < _pack.size()) {
if (auto main = App::main()) {
if (main->onSendSticker(_pack.at(index))) {
Ui::hideSettingsAndLayer();
}
}
}
}
}
void StickerSetInner::updateSelected() {
auto index = stickerFromGlobalPos(QCursor::pos());
if (index != _selected) {
startOverAnimation(_selected, 1., 0.);
_selected = index;
startOverAnimation(_selected, 0., 1.);
setCursor(_selected >= 0 ? style::cur_pointer : style::cur_default);
}
}
void StickerSetInner::startOverAnimation(int index, float64 from, float64 to) {
if (index >= 0 && index < _packOvers.size()) {
START_ANIMATION(_packOvers[index], func([this, index]() {
int row = index / StickerPanPerRow;
int column = index % StickerPanPerRow;
int left = st::stickersPadding.left() + column * st::stickersSize.width();
int top = st::stickersPadding.top() + row * st::stickersSize.height();
rtlupdate(left, top, st::stickersSize.width(), st::stickersSize.height());
}), from, to, st::emojiPanDuration, anim::linear);
}
}
void StickerSetInner::onPreview() {
int32 index = stickerFromGlobalPos(QCursor::pos());
int index = stickerFromGlobalPos(QCursor::pos());
if (index >= 0 && index < _pack.size()) {
_previewShown = index;
Ui::showMediaPreview(_pack.at(_previewShown));
@ -271,10 +287,19 @@ void StickerSetInner::paintEvent(QPaintEvent *e) {
for (int32 j = 0; j < StickerPanPerRow; ++j) {
int32 index = i * StickerPanPerRow + j;
if (index >= _pack.size()) break;
t_assert(index < _packOvers.size());
DocumentData *doc = _pack.at(index);
QPoint pos(st::stickersPadding.left() + j * st::stickersSize.width(), st::stickersPadding.top() + i * st::stickersSize.height());
if (auto over = _packOvers[index].current((index == _selected) ? 1. : 0.)) {
p.setOpacity(over);
QPoint tl(pos);
if (rtl()) tl.setX(width() - tl.x() - st::stickersSize.width());
App::roundRect(p, QRect(tl, st::stickersSize), st::emojiPanHover, StickerHoverCorners);
p.setOpacity(1);
}
bool goodThumb = !doc->thumb->isNull() && ((doc->thumb->width() >= 128) || (doc->thumb->height() >= 128));
if (goodThumb) {
doc->thumb->load();
@ -302,10 +327,9 @@ void StickerSetInner::paintEvent(QPaintEvent *e) {
}
}
void StickerSetInner::setScrollBottom(int32 bottom) {
if (bottom == _bottom) return;
_bottom = bottom;
void StickerSetInner::setVisibleTopBottom(int visibleTop, int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
}
bool StickerSetInner::loaded() const {
@ -357,7 +381,7 @@ StickerSetBox::StickerSetBox(const MTPInputStickerSet &set) : ScrollableBox(st::
connect(&_done, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(&_inner, SIGNAL(installed(uint64)), this, SLOT(onInstalled(uint64)));
@ -394,7 +418,9 @@ void StickerSetBox::onUpdateButtons() {
}
void StickerSetBox::onScroll() {
_inner.setScrollBottom(_scroll.scrollTop() + _scroll.height());
auto scroll = scrollArea();
auto scrollTop = scroll->scrollTop();
_inner.setVisibleTopBottom(scrollTop, scrollTop + scroll->height());
}
void StickerSetBox::showAll() {
@ -454,7 +480,7 @@ void StickerSetBox::resizeEvent(QResizeEvent *e) {
namespace internal {
StickersInner::StickersInner(StickersBox::Section section) : TWidget()
StickersInner::StickersInner(StickersBox::Section section) : ScrolledWidget()
, _section(section)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _a_shifting(animation(this, &StickersInner::step_shifting))
@ -467,7 +493,7 @@ StickersInner::StickersInner(StickersBox::Section section) : TWidget()
setup();
}
StickersInner::StickersInner(const Stickers::Order &archivedIds) : TWidget()
StickersInner::StickersInner(const Stickers::Order &archivedIds) : ScrolledWidget()
, _section(StickersBox::Section::ArchivedPart)
, _archivedIds(archivedIds)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
@ -482,10 +508,15 @@ StickersInner::StickersInner(const Stickers::Order &archivedIds) : TWidget()
}
void StickersInner::setup() {
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(onImageLoaded()));
setMouseTracking(true);
}
void StickersInner::onImageLoaded() {
update();
readVisibleSets();
}
void StickersInner::paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const {
if (selected) {
p.fillRect(0, y, width(), _buttonHeight, st::contactsBgOver);
@ -618,18 +649,18 @@ void StickersInner::paintRow(Painter &p, int32 index) {
int statusx = namex;
int statusy = st::contactsPadding.top() + st::contactsStatusTop;
p.setFont(st::contactsNameFont);
p.setPen(st::black);
p.drawTextLeft(namex, namey, width(), s->title, s->titleWidth);
if (s->unread) {
p.setPen(Qt::NoPen);
p.setBrush(st::stickersFeaturedUnreadBg);
p.setRenderHint(QPainter::HighQualityAntialiasing, true);
p.drawEllipse(rtlrect(namex, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width()));
p.drawEllipse(rtlrect(namex + s->titleWidth + st::stickersFeaturedUnreadSkip, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width()));
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
namex += st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
}
p.setFont(st::contactsNameFont);
p.setPen(st::black);
p.drawTextLeft(namex, namey, width(), s->title);
p.setFont(st::contactsStatusFont);
p.setPen(st::contactsStatusFg);
@ -760,7 +791,8 @@ void StickersInner::onClearRecent() {
emit App::main()->updateStickers();
rebuild();
MTP::send(MTPmessages_ClearRecentStickers());
MTPmessages_ClearRecentStickers::Flags flags = 0;
MTP::send(MTPmessages_ClearRecentStickers(MTP_flags(flags)));
}
void StickersInner::onClearBoxDestroyed(QObject *box) {
@ -849,59 +881,13 @@ void StickersInner::installSet(uint64 setId) {
MTP::send(MTPmessages_InstallStickerSet(Stickers::inputSetId(*it), MTP_boolFalse()), rpcDone(&StickersInner::installDone), rpcFail(&StickersInner::installFail, setId));
auto flags = it->flags;
it->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread);
it->flags |= MTPDstickerSet::Flag::f_installed;
auto changedFlags = flags ^ it->flags;
auto &order = Global::RefStickerSetsOrder();
int insertAtIndex = 0, currentIndex = order.indexOf(setId);
if (currentIndex != insertAtIndex) {
if (currentIndex > 0) {
order.removeAt(currentIndex);
}
order.insert(insertAtIndex, setId);
}
auto custom = sets.find(Stickers::CustomSetId);
if (custom != sets.cend()) {
for_const (auto sticker, it->stickers) {
int removeIndex = custom->stickers.indexOf(sticker);
if (removeIndex >= 0) custom->stickers.removeAt(removeIndex);
}
if (custom->stickers.isEmpty()) {
sets.erase(custom);
}
}
Local::writeInstalledStickers();
if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) Local::writeFeaturedStickers();
if (changedFlags & MTPDstickerSet::Flag::f_archived) {
auto index = Global::RefArchivedStickerSetsOrder().indexOf(setId);
if (index >= 0) {
Global::RefArchivedStickerSetsOrder().removeAt(index);
Local::writeArchivedStickers();
}
}
emit App::main()->stickersUpdated();
Stickers::installLocally(setId);
}
void StickersInner::installDone(const MTPmessages_StickerSetInstallResult &result) {
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
Stickers::applyArchivedResult(result.c_messages_stickerSetInstallResultArchive());
Local::writeInstalledStickers();
Local::writeArchivedStickers();
emit App::main()->stickersUpdated();
}
// TEST DATA ONLY
//MTPVector<MTPStickerSet> v = MTP_vector<MTPStickerSet>(0);
//for (auto &set : Global::RefStickerSets()) {
// if (rand() < RAND_MAX / 2) {
// set.flags |= MTPDstickerSet::Flag::f_archived;
// v._vector().v.push_back(MTP_stickerSet(MTP_flags(set.flags), MTP_long(set.id), MTP_long(set.access), MTP_string(set.title), MTP_string(set.shortName), MTP_int(set.count), MTP_int(set.hash)));
// }
//}
//Stickers::applyArchivedResult(MTP_messages_stickerSetInstallResultArchive(v).c_messages_stickerSetInstallResultArchive());
}
bool StickersInner::installFail(uint64 setId, const RPCError &error) {
@ -914,19 +900,7 @@ bool StickersInner::installFail(uint64 setId, const RPCError &error) {
return true;
}
it->flags &= ~MTPDstickerSet::Flag::f_installed;
auto &order = Global::RefStickerSetsOrder();
int currentIndex = order.indexOf(setId);
if (currentIndex >= 0) {
order.removeAt(currentIndex);
}
Local::writeInstalledStickers();
emit App::main()->stickersUpdated();
Ui::showLayer(new InformBox(lang(lng_stickers_not_found)), KeepOtherLayers);
Stickers::undoInstallLocally(setId);
return true;
}
@ -1056,14 +1030,6 @@ void StickersInner::rebuild() {
}
App::api()->requestStickerSets();
updateSize();
if (_section == Section::Featured && Global::FeaturedStickerSetsUnreadCount()) {
Global::SetFeaturedStickerSetsUnreadCount(0);
for (auto &set : Global::RefStickerSets()) {
set.flags &= ~MTPDstickerSet_ClientFlag::f_unread;
}
MTP::send(MTPmessages_ReadFeaturedStickers(), rpcDone(&StickersInner::readFeaturedDone), rpcFail(&StickersInner::readFeaturedFail));
}
}
void StickersInner::updateSize() {
@ -1091,7 +1057,7 @@ void StickersInner::updateRows() {
if (_section == Section::Installed) {
row->disabled = false;
}
row->title = fillSetTitle(set, maxNameWidth);
row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth);
row->count = fillSetCount(set);
}
}
@ -1115,6 +1081,7 @@ int StickersInner::countMaxNameWidth() const {
namew -= qMax(qMax(qMax(_returnWidth, _removeWidth), _restoreWidth), _clearWidth);
} else {
namew -= st::stickersAddIcon.width() - st::defaultActiveButton.width;
namew -= st::stickersFeaturedUnreadSize + st::stickersFeaturedUnreadSkip;
}
return namew;
}
@ -1130,10 +1097,11 @@ void StickersInner::rebuildAppendSet(const Stickers::Set &set, int maxNameWidth)
int pixw = 0, pixh = 0;
fillSetCover(set, &sticker, &pixw, &pixh);
QString title = fillSetTitle(set, maxNameWidth);
int titleWidth = 0;
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
int count = fillSetCount(set);
_rows.push_back(new StickerSetRow(set.id, sticker, count, title, installed, official, unread, disabled, recent, pixw, pixh));
_rows.push_back(new StickerSetRow(set.id, sticker, count, title, titleWidth, installed, official, unread, disabled, recent, pixw, pixh));
_animStartTimes.push_back(0);
}
@ -1181,11 +1149,15 @@ int StickersInner::fillSetCount(const Stickers::Set &set) const {
return result + added;
}
QString StickersInner::fillSetTitle(const Stickers::Set &set, int maxNameWidth) const {
QString StickersInner::fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const {
auto result = set.title;
int32 titleWidth = st::contactsNameFont->width(result);
int titleWidth = st::contactsNameFont->width(result);
if (titleWidth > maxNameWidth) {
result = st::contactsNameFont->elided(result, maxNameWidth);
titleWidth = st::contactsNameFont->width(result);
}
if (outTitleWidth) {
*outTitleWidth = titleWidth;
}
return result;
}
@ -1201,33 +1173,9 @@ void StickersInner::fillSetFlags(const Stickers::Set &set, bool *outRecent, bool
*outOfficial = (set.flags & MTPDstickerSet::Flag::f_official);
*outDisabled = (set.flags & MTPDstickerSet::Flag::f_archived);
if (_section == Section::Featured) {
*outUnread = _unreadSets.contains(set.id);
if (!*outUnread && (set.flags & MTPDstickerSet_ClientFlag::f_unread)) {
*outUnread = true;
_unreadSets.insert(set.id);
*outUnread = (set.flags & MTPDstickerSet_ClientFlag::f_unread);
}
}
}
}
void StickersInner::readFeaturedDone(const MTPBool &result) {
Local::writeFeaturedStickers();
emit App::main()->stickersUpdated();
}
bool StickersInner::readFeaturedFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
int unreadCount = 0;
for_const (auto &set, Global::StickerSets()) {
if (!(set.flags & MTPDstickerSet::Flag::f_installed)) {
if (set.flags & MTPDstickerSet_ClientFlag::f_unread) {
++unreadCount;
}
}
}
Global::SetFeaturedStickerSetsUnreadCount(unreadCount);
return true;
}
Stickers::Order StickersInner::getOrder() const {
@ -1253,6 +1201,32 @@ Stickers::Order StickersInner::getDisabledSets() const {
return result;
}
void StickersInner::setVisibleTopBottom(int visibleTop, int visibleBottom) {
if (_section == Section::Featured) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
readVisibleSets();
}
}
void StickersInner::readVisibleSets() {
auto itemsVisibleTop = _visibleTop - _itemsTop;
auto itemsVisibleBottom = _visibleBottom - _itemsTop;
int rowFrom = floorclamp(itemsVisibleTop, _rowHeight, 0, _rows.size());
int rowTo = ceilclamp(itemsVisibleBottom, _rowHeight, 0, _rows.size());
for (int i = rowFrom; i < rowTo; ++i) {
if (!_rows[i]->unread) {
continue;
}
if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) {
continue;
}
if (!_rows[i]->sticker || _rows[i]->sticker->thumb->loaded() || _rows[i]->sticker->loaded()) {
Stickers::markFeaturedAsRead(_rows[i]->id);
}
}
}
void StickersInner::setVisibleScrollbar(int32 width) {
_scrollbar = width;
}
@ -1299,9 +1273,24 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti
bool addedSet = false;
auto &v = stickers.vsets.c_vector().v;
for_const (auto &stickerSet, v) {
if (stickerSet.type() != mtpc_stickerSetCovered || stickerSet.c_stickerSetCovered().vset.type() != mtpc_stickerSet) continue;
const MTPDstickerSet *setData = nullptr;
switch (stickerSet.type()) {
case mtpc_stickerSetCovered: {
auto &d = stickerSet.c_stickerSetCovered();
if (d.vset.type() == mtpc_stickerSet) {
setData = &d.vset.c_stickerSet();
}
} break;
case mtpc_stickerSetMultiCovered: {
auto &d = stickerSet.c_stickerSetMultiCovered();
if (d.vset.type() == mtpc_stickerSet) {
setData = &d.vset.c_stickerSet();
}
} break;
}
if (!setData) continue;
if (auto set = Stickers::feedSet(stickerSet.c_stickerSetCovered().vset.c_stickerSet())) {
if (auto set = Stickers::feedSet(*setData)) {
auto index = archived.indexOf(set->id);
if (archived.isEmpty() || index != archived.size() - 1) {
if (index < archived.size() - 1) {
@ -1326,7 +1315,7 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti
if (addedSet) {
_inner->updateSize();
setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight)));
_inner->setVisibleScrollbar((_scroll.scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0);
_inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0);
App::api()->requestStickerSets();
} else {
_allArchivedLoaded = v.isEmpty() || (offsetId != 0);
@ -1389,7 +1378,7 @@ void StickersBox::setup() {
connect(_inner, SIGNAL(checkDraggingScroll(int)), this, SLOT(onCheckDraggingScroll(int)));
connect(_inner, SIGNAL(noDraggingScroll()), this, SLOT(onNoDraggingScroll()));
connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer()));
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(scrollArea(), SIGNAL(scrolled()), this, SLOT(onScroll()));
_scrollTimer.setSingleShot(false);
rebuildList();
@ -1398,14 +1387,21 @@ void StickersBox::setup() {
}
void StickersBox::onScroll() {
updateVisibleTopBottom();
checkLoadMoreArchived();
}
void StickersBox::updateVisibleTopBottom() {
auto visibleTop = scrollArea()->scrollTop();
auto visibleBottom = visibleTop + scrollArea()->height();
_inner->setVisibleTopBottom(visibleTop, visibleBottom);
}
void StickersBox::checkLoadMoreArchived() {
if (_section != Section::Archived) return;
int scrollTop = _scroll.scrollTop(), scrollTopMax = _scroll.scrollTopMax();
if (scrollTop + PreloadHeightsCount * _scroll.height() >= scrollTopMax) {
int scrollTop = scrollArea()->scrollTop(), scrollTopMax = scrollArea()->scrollTopMax();
if (scrollTop + PreloadHeightsCount * scrollArea()->height() >= scrollTopMax) {
if (!_archivedRequestId && !_allArchivedLoaded) {
uint64 lastId = 0;
for (auto setId = Global::ArchivedStickerSetsOrder().cend(), e = Global::ArchivedStickerSetsOrder().cbegin(); setId != e;) {
@ -1452,10 +1448,12 @@ void StickersBox::saveOrder() {
if (order.size() > 1) {
QVector<MTPlong> mtpOrder;
mtpOrder.reserve(order.size());
for (int32 i = 0, l = order.size(); i < l; ++i) {
for (int i = 0, l = order.size(); i < l; ++i) {
mtpOrder.push_back(MTP_long(order.at(i)));
}
_reorderRequest = MTP::send(MTPmessages_ReorderStickerSets(MTP_vector<MTPlong>(mtpOrder)), rpcDone(&StickersBox::reorderDone), rpcFail(&StickersBox::reorderFail));
MTPmessages_ReorderStickerSets::Flags flags = 0;
_reorderRequest = MTP::send(MTPmessages_ReorderStickerSets(MTP_flags(flags), MTP_vector<MTPlong>(mtpOrder)), rpcDone(&StickersBox::reorderDone), rpcFail(&StickersBox::reorderFail));
} else {
reorderDone(MTP_boolTrue());
}
@ -1522,7 +1520,8 @@ StickersBox::~StickersBox() {
void StickersBox::resizeEvent(QResizeEvent *e) {
ItemListBox::resizeEvent(e);
_inner->resize(width(), _inner->height());
_inner->setVisibleScrollbar((_scroll.scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0);
_inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0);
updateVisibleTopBottom();
if (_topShadow) {
_topShadow->setGeometry(0, st::boxTitleHeight + _aboutHeight, width(), st::lineWidth);
}
@ -1546,14 +1545,14 @@ void StickersBox::onStickersUpdated() {
void StickersBox::rebuildList() {
_inner->rebuild();
setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight)));
_inner->setVisibleScrollbar((_scroll.scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0);
_inner->setVisibleScrollbar((scrollArea()->scrollTopMax() > 0) ? (st::boxScroll.width - st::boxScroll.deltax) : 0);
}
void StickersBox::onCheckDraggingScroll(int localY) {
if (localY < _scroll.scrollTop()) {
_scrollDelta = localY - _scroll.scrollTop();
} else if (localY >= _scroll.scrollTop() + _scroll.height()) {
_scrollDelta = localY - _scroll.scrollTop() - _scroll.height() + 1;
if (localY < scrollArea()->scrollTop()) {
_scrollDelta = localY - scrollArea()->scrollTop();
} else if (localY >= scrollArea()->scrollTop() + scrollArea()->height()) {
_scrollDelta = localY - scrollArea()->scrollTop() - scrollArea()->height() + 1;
} else {
_scrollDelta = 0;
}
@ -1570,7 +1569,7 @@ void StickersBox::onNoDraggingScroll() {
void StickersBox::onScrollTimer() {
int32 d = (_scrollDelta > 0) ? qMin(_scrollDelta * 3 / 20 + 1, int32(MaxScrollSpeed)) : qMax(_scrollDelta * 3 / 20 - 1, -int32(MaxScrollSpeed));
_scroll.scrollToY(_scroll.scrollTop() + d);
scrollArea()->scrollToY(scrollArea()->scrollTop() + d);
}
void StickersBox::onSave() {

View file

@ -24,42 +24,40 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
class ConfirmBox;
class StickerSetInner : public TWidget, public RPCSender {
class StickerSetInner : public ScrolledWidget, public RPCSender {
Q_OBJECT
public:
StickerSetInner(const MTPInputStickerSet &set);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void paintEvent(QPaintEvent *e);
bool loaded() const;
int32 notInstalled() const;
bool official() const;
QString title() const;
QString shortName() const;
void setScrollBottom(int32 bottom);
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void install();
~StickerSetInner();
public slots:
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private slots:
void onPreview();
signals:
void updateButtons();
void installed(uint64 id);
private:
int32 stickerFromGlobalPos(const QPoint &p) const;
void updateSelected();
void startOverAnimation(int index, float64 from, float64 to);
int stickerFromGlobalPos(const QPoint &p) const;
void gotSet(const MTPmessages_StickerSet &set);
bool failedSet(const RPCError &error);
@ -67,6 +65,7 @@ private:
void installDone(const MTPmessages_StickerSetInstallResult &result);
bool installFail(const RPCError &error);
QVector<FloatAnimation> _packOvers;
StickerPack _pack;
StickersByEmojiMap _emoji;
bool _loaded = false;
@ -77,13 +76,17 @@ private:
int32 _setHash = 0;
MTPDstickerSet::Flags _setFlags = 0;
int32 _bottom = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
MTPInputStickerSet _input;
mtpRequestId _installRequest = 0;
int _selected = -1;
QTimer _previewTimer;
int32 _previewShown = -1;
int _previewShown = -1;
};
class StickerSetBox : public ScrollableBox, public RPCSender {
@ -169,6 +172,7 @@ private:
bool reorderFail(const RPCError &result);
void saveOrder();
void updateVisibleTopBottom();
void checkLoadMoreArchived();
void getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedStickers &result);
@ -198,7 +202,7 @@ int32 stickerPacksCount(bool includeDisabledOfficial = false);
namespace internal {
class StickersInner : public TWidget, public RPCSender {
class StickersInner : public ScrolledWidget, public RPCSender {
Q_OBJECT
public:
@ -220,6 +224,7 @@ public:
Stickers::Order getDisabledSets() const;
void setVisibleScrollbar(int32 width);
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
~StickersInner();
@ -239,6 +244,9 @@ public slots:
void onClearRecent();
void onClearBoxDestroyed(QObject *box);
private slots:
void onImageLoaded();
private:
void setup();
void paintButton(Painter &p, int y, bool selected, const QString &text, int badgeCounter) const;
@ -249,21 +257,22 @@ private:
void setActionSel(int32 actionSel);
float64 aboveShadowOpacity() const;
void readVisibleSets();
void installSet(uint64 setId);
void installDone(const MTPmessages_StickerSetInstallResult &result);
bool installFail(uint64 setId, const RPCError &error);
void readFeaturedDone(const MTPBool &result);
bool readFeaturedFail(const RPCError &error);
Section _section;
Stickers::Order _archivedIds;
int32 _rowHeight;
struct StickerSetRow {
StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, bool installed, bool official, bool unread, bool disabled, bool recent, int32 pixw, int32 pixh) : id(id)
StickerSetRow(uint64 id, DocumentData *sticker, int32 count, const QString &title, int titleWidth, bool installed, bool official, bool unread, bool disabled, bool recent, int32 pixw, int32 pixh) : id(id)
, sticker(sticker)
, count(count)
, title(title)
, titleWidth(titleWidth)
, installed(installed)
, official(official)
, unread(unread)
@ -277,6 +286,7 @@ private:
DocumentData *sticker;
int32 count;
QString title;
int titleWidth;
bool installed, official, unread, disabled, recent;
int32 pixw, pixh;
anim::ivalue yadd;
@ -286,7 +296,7 @@ private:
void rebuildAppendSet(const Stickers::Set &set, int maxNameWidth);
void fillSetCover(const Stickers::Set &set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
int fillSetCount(const Stickers::Set &set) const;
QString fillSetTitle(const Stickers::Set &set, int maxNameWidth) const;
QString fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const;
void fillSetFlags(const Stickers::Set &set, bool *outRecent, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outDisabled);
int countMaxNameWidth() const;
@ -297,7 +307,9 @@ private:
anim::fvalue _aboveShadowFadeOpacity = { 0., 0. };
Animation _a_shifting;
int32 _itemsTop;
int _visibleTop = 0;
int _visibleBottom = 0;
int _itemsTop = 0;
bool _saving = false;
@ -312,9 +324,6 @@ private:
bool _hasFeaturedButton = false;
bool _hasArchivedButton = false;
// Remember all the unread set ids to display unread dots.
OrderedSet<uint64> _unreadSets;
QPoint _mouse;
int _selected = -3; // -2 - featured stickers button, -1 - archived stickers button
int _pressed = -2;

View file

@ -1275,8 +1275,8 @@ public:
template <typename R, typename... Args>
class NullFunctionImplementation : public FunctionImplementation<R, Args...> {
public:
virtual R call(Args... args) { return R(); }
virtual void destroy() {}
R call(Args... args) override { return R(); }
void destroy() override {}
static NullFunctionImplementation<R, Args...> SharedInstance;
};
@ -1325,7 +1325,7 @@ class WrappedFunction : public FunctionImplementation<R, Args...> {
public:
using Method = R(*)(Args... args);
WrappedFunction(Method method) : _method(method) {}
virtual R call(Args... args) { return (*_method)(args...); }
R call(Args... args) override { return (*_method)(args...); }
private:
Method _method;
@ -1341,7 +1341,7 @@ class ObjectFunction : public FunctionImplementation<R, Args...> {
public:
using Method = R(I::*)(Args... args);
ObjectFunction(O *obj, Method method) : _obj(obj), _method(method) {}
virtual R call(Args... args) { return (_obj->*_method)(args...); }
R call(Args... args) override { return (_obj->*_method)(args...); }
private:
O *_obj;

View file

@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/confirmbox.h"
#include "core/qthelp_regex.h"
#include "core/qthelp_url.h"
#include "localstorage.h"
QString UrlClickHandler::copyToClipboardContextItemText() const {
return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link);
@ -113,6 +114,20 @@ void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const {
}
}
void BotGameUrlClickHandler::onClick(Qt::MouseButton button) const {
auto u = url();
u = tryConvertUrlToLocal(u);
if (u.startsWith(qstr("tg://"))) {
App::openLocalUrl(u);
} else if (!_bot || Local::isBotTrusted(_bot)) {
doOpen(u);
} else {
Ui::showLayer(new ConfirmBotGameBox(_bot, u));
}
}
QString HiddenUrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const {
QString result;
if (mode == ExpandLinksAll) {

View file

@ -122,6 +122,17 @@ public:
};
class BotGameUrlClickHandler : public UrlClickHandler {
public:
BotGameUrlClickHandler(UserData *bot, QString url) : UrlClickHandler(url, false), _bot(bot) {
}
void onClick(Qt::MouseButton button) const override;
private:
UserData *_bot;
};
class MentionClickHandler : public TextClickHandler {
public:
MentionClickHandler(const QString &tag) : _tag(tag) {

View file

@ -372,3 +372,42 @@ public:
};
} // namespace base
// While we still use Function<>
template <typename FunctionType>
struct LambdaFunctionHelper;
template <typename Lambda, typename R, typename ...Args>
struct LambdaFunctionHelper<R(Lambda::*)(Args...) const> {
using FunctionType = Function<R, Args...>;
using UniqueType = base::lambda_unique<R(Args...)>;
};
template <typename FunctionType>
using LambdaGetFunction = typename LambdaFunctionHelper<FunctionType>::FunctionType;
template <typename FunctionType>
using LambdaGetUnique = typename LambdaFunctionHelper<FunctionType>::UniqueType;
template <typename R, typename ...Args>
class LambdaFunctionImplementation : public FunctionImplementation<R, Args...> {
public:
LambdaFunctionImplementation(base::lambda_unique<R(Args...)> &&lambda) : _lambda(std_::move(lambda)) {
}
R call(Args... args) override { return _lambda(std_::forward<Args>(args)...); }
private:
base::lambda_unique<R(Args...)> _lambda;
};
template <typename R, typename ...Args>
inline Function<R, Args...> lambda_wrap_helper(base::lambda_unique<R(Args...)> &&lambda) {
return Function<R, Args...>(new LambdaFunctionImplementation<R, Args...>(std_::move(lambda)));
}
template <typename Lambda, typename = std_::enable_if_t<std_::is_rvalue_reference<Lambda&&>::value>>
inline LambdaGetFunction<decltype(&Lambda::operator())> func(Lambda &&lambda) {
return lambda_wrap_helper(LambdaGetUnique<decltype(&Lambda::operator())>(std_::move(lambda)));
}

View file

@ -71,6 +71,16 @@ void IndexedList::adjustByPos(const RowsByLetter &links) {
}
}
void IndexedList::moveToTop(PeerData *peer) {
if (_list.moveToTop(peer->id)) {
for_const (auto ch, peer->chars) {
if (auto list = _index.value(ch)) {
list->moveToTop(peer->id);
}
}
}
}
void IndexedList::peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
t_assert(_sortMode != SortMode::Date);
if (_sortMode == SortMode::Name) {

View file

@ -34,6 +34,7 @@ public:
RowsByLetter addToEnd(History *history);
Row *addByName(History *history);
void adjustByPos(const RowsByLetter &links);
void moveToTop(PeerData *peer);
// For sortMode != SortMode::Date
void peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);

View file

@ -189,6 +189,14 @@ void List::adjustByPos(Row *row) {
}
}
bool List::moveToTop(PeerId peerId) {
auto i = _rowByPeer.find(peerId);
if (i == _rowByPeer.cend()) return false;
insertBefore(i.value(), _begin);
return true;
}
bool List::del(PeerId peerId, Row *replacedBy) {
auto i = _rowByPeer.find(peerId);
if (i == _rowByPeer.cend()) return false;

View file

@ -53,10 +53,9 @@ public:
void paint(Painter &p, int32 w, int32 hFrom, int32 hTo, PeerData *act, PeerData *sel, bool onlyBackground) const;
Row *addToEnd(History *history);
bool insertBefore(Row *row, Row *before);
bool insertAfter(Row *row, Row *after);
Row *adjustByName(const PeerData *peer);
Row *addByName(History *history);
bool moveToTop(PeerId peerId);
void adjustByPos(Row *row);
bool del(PeerId peerId, Row *replacedBy = nullptr);
void remove(Row *row);
@ -114,6 +113,8 @@ public:
private:
void adjustCurrent(int y, int h) const;
bool insertBefore(Row *row, Row *before);
bool insertAfter(Row *row, Row *after);
static Row *next(Row *row) {
return row->_next;
}

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,6 @@ class Dropdown : public TWidget {
Q_OBJECT
public:
Dropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef);
IconedButton *addButton(IconedButton *button);
@ -61,11 +60,9 @@ public:
}
signals:
void hiding();
public slots:
void hideStart();
void hideFinish();
@ -75,20 +72,19 @@ public slots:
void buttonStateChanged(int oldState, ButtonStateChangeSource source);
private:
void adjustButtons();
bool _ignore;
bool _ignore = false;
typedef QVector<IconedButton*> Buttons;
Buttons _buttons;
int32 _selected;
int32 _selected = -1;
const style::dropdown &_st;
int32 _width, _height;
bool _hiding;
bool _hiding = false;
anim::fvalue a_opacity;
Animation _a_appearance;
@ -103,7 +99,6 @@ class DragArea : public TWidget {
Q_OBJECT
public:
DragArea(QWidget *parent);
void paintEvent(QPaintEvent *e);
@ -133,18 +128,15 @@ public:
}
signals:
void dropped(const QMimeData *data);
public slots:
void hideStart();
void hideFinish();
void showStart();
private:
bool _hiding, _in;
anim::fvalue a_opacity;
@ -156,588 +148,3 @@ private:
QString _text, _subtext;
};
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
namespace internal {
constexpr int InlineItemsMaxPerRow = 5;
constexpr int EmojiColorsCount = 5;
using InlineResult = InlineBots::Result;
using InlineResults = QList<InlineBots::Result*>;
using InlineItem = InlineBots::Layout::ItemBase;
struct InlineCacheEntry {
~InlineCacheEntry() {
clearResults();
}
QString nextOffset;
QString switchPmText, switchPmStartToken;
InlineResults results; // owns this results list
void clearResults();
};
class EmojiColorPicker : public TWidget {
Q_OBJECT
public:
EmojiColorPicker();
void showEmoji(uint32 code);
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void step_appearance(float64 ms, bool timer);
void step_selected(uint64 ms, bool timer);
void showStart();
void clearSelection(bool fast = false);
public slots:
void hideStart(bool fast = false);
signals:
void emojiSelected(EmojiPtr emoji);
void hidden();
private:
void drawVariant(Painter &p, int variant);
void updateSelected();
bool _ignoreShow;
EmojiPtr _variants[EmojiColorsCount + 1];
typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
EmojiAnimations _emojiAnimations;
Animation _a_selected;
float64 _hovers[EmojiColorsCount + 1];
int32 _selected, _pressedSel;
QPoint _lastMousePos;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
Animation _a_appearance;
QTimer _hideTimer;
BoxShadow _shadow;
};
class EmojiPanel;
class EmojiPanInner : public TWidget {
Q_OBJECT
public:
EmojiPanInner();
void setMaxHeight(int32 h);
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEvent(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void enterFromChildEvent(QEvent *e, QWidget *child) override;
void step_selected(uint64 ms, bool timer);
void hideFinish();
void showEmojiPack(DBIEmojiTab packIndex);
void clearSelection(bool fast = false);
DBIEmojiTab currentTab(int yOffset) const;
void refreshRecent();
void setScrollTop(int top);
void fillPanels(QVector<EmojiPanel*> &panels);
void refreshPanels(QVector<EmojiPanel*> &panels);
public slots:
void updateSelected();
void onShowPicker();
void onPickerHidden();
void onColorSelected(EmojiPtr emoji);
bool checkPickerHide();
signals:
void selected(EmojiPtr emoji);
void switchToStickers();
void scrollToY(int y);
void disableScroll(bool dis);
void needRefreshPanels();
void saveConfigDelayed(int32 delay);
private:
int32 _maxHeight;
int32 countHeight();
void selectEmoji(EmojiPtr emoji);
QRect emojiRect(int tab, int sel);
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _animations;
Animation _a_selected;
int32 _top, _counts[emojiTabCount];
QVector<EmojiPtr> _emojis[emojiTabCount];
QVector<float64> _hovers[emojiTabCount];
int32 _esize;
int32 _selected, _pressedSel, _pickerSel;
QPoint _lastMousePos;
EmojiColorPicker _picker;
QTimer _showPickerTimer;
};
struct StickerIcon {
StickerIcon(uint64 setId) : setId(setId), sticker(0), pixw(0), pixh(0) {
}
StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) {
}
uint64 setId;
DocumentData *sticker;
int32 pixw, pixh;
};
class StickerPanInner : public TWidget {
Q_OBJECT
public:
StickerPanInner();
void setMaxHeight(int32 h);
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEvent(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void enterFromChildEvent(QEvent *e, QWidget *child) override;
void step_selected(uint64 ms, bool timer);
void hideFinish(bool completely);
void showFinish();
void showStickerSet(uint64 setId);
void updateShowingSavedGifs();
bool showSectionIcons() const;
void clearSelection(bool fast = false);
void refreshStickers();
void refreshRecentStickers(bool resize = true);
void refreshSavedGifs();
int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted);
void refreshRecent();
void inlineBotChanged();
void hideInlineRowsPanel();
void clearInlineRowsPanel();
void fillIcons(QList<StickerIcon> &icons);
void fillPanels(QVector<EmojiPanel*> &panels);
void refreshPanels(QVector<EmojiPanel*> &panels);
void setScrollTop(int top);
void preloadImages();
uint64 currentSet(int yOffset) const;
void notify_inlineItemLayoutChanged(const InlineItem *layout);
void ui_repaintInlineItem(const InlineItem *layout);
bool ui_isInlineItemVisible(const InlineItem *layout);
bool ui_isInlineItemBeingChosen();
bool inlineResultsShown() const {
return _showingInlineItems && !_showingSavedGifs;
}
int32 countHeight(bool plain = false);
~StickerPanInner();
private slots:
void updateSelected();
void onSettings();
void onPreview();
void onUpdateInlineItems();
void onSwitchPm();
signals:
void selected(DocumentData *sticker);
void selected(PhotoData *photo);
void selected(InlineBots::Result *result, UserData *bot);
void removing(quint64 setId);
void refreshIcons();
void emptyInlineRows();
void switchToEmoji();
void scrollToY(int y);
void scrollUpdated();
void disableScroll(bool dis);
void needRefreshPanels();
void saveConfigDelayed(int32 delay);
private:
void paintInlineItems(Painter &p, const QRect &r);
void paintStickers(Painter &p, const QRect &r);
void refreshSwitchPmButton(const InlineCacheEntry *entry);
void appendSet(uint64 setId);
void selectEmoji(EmojiPtr emoji);
QRect stickerRect(int tab, int sel);
int32 _maxHeight;
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _animations;
Animation _a_selected;
int32 _top;
struct DisplayedSet {
DisplayedSet(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) {
}
uint64 id;
MTPDstickerSet::Flags flags;
QString title;
QVector<float64> hovers;
StickerPack pack;
};
QList<DisplayedSet> _sets;
QList<bool> _custom;
bool _showingSavedGifs, _showingInlineItems;
bool _setGifCommand;
UserData *_inlineBot;
QString _inlineBotTitle;
uint64 _lastScrolled;
QTimer _updateInlineItems;
bool _inlineWithThumb;
std_::unique_ptr<BoxButton> _switchPmButton;
QString _switchPmStartToken;
typedef QVector<InlineItem*> InlineItems;
struct InlineRow {
InlineRow() : height(0) {
}
int32 height;
InlineItems items;
};
typedef QVector<InlineRow> InlineRows;
InlineRows _inlineRows;
void clearInlineRows(bool resultsDeleted);
using GifLayouts = QMap<DocumentData*, InlineItem*>;
GifLayouts _gifLayouts;
InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position);
using InlineLayouts = QMap<InlineResult*, InlineItem*>;
InlineLayouts _inlineLayouts;
InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position);
bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth);
bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false);
InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0);
void deleteUnusedGifLayouts();
void deleteUnusedInlineLayouts();
int32 validateExistingInlineRows(const InlineResults &results);
int32 _selected, _pressedSel;
QPoint _lastMousePos;
LinkButton _settings;
QTimer _previewTimer;
bool _previewShown;
};
class EmojiPanel : public TWidget {
Q_OBJECT
public:
EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY); // Stickers::NoneSetId if in emoji
void setText(const QString &text);
void setDeleteVisible(bool isVisible);
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
int32 wantedY() const {
return _wantedY;
}
void setWantedY(int32 y) {
_wantedY = y;
}
signals:
void deleteClicked(quint64 setId);
void mousePressed();
public slots:
void onDelete();
private:
void updateText();
int32 _wantedY;
QString _text, _fullText;
uint64 _setId;
bool _special, _deleteVisible;
IconedButton *_delete;
};
class EmojiSwitchButton : public Button {
public:
EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji
void paintEvent(QPaintEvent *e);
void updateText(const QString &inlineBotUsername = QString());
protected:
bool _toStickers;
QString _text;
int32 _textWidth;
};
} // namespace internal
class EmojiPan : public TWidget, public RPCSender {
Q_OBJECT
public:
EmojiPan(QWidget *parent);
void setMaxHeight(int32 h);
void paintEvent(QPaintEvent *e);
void moveBottom(int32 bottom, bool force = false);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void otherEnter();
void otherLeave();
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
bool event(QEvent *e);
void fastHide();
bool hiding() const {
return _hiding || _hideTimer.isActive();
}
void step_appearance(float64 ms, bool timer);
void step_slide(float64 ms, bool timer);
void step_icons(uint64 ms, bool timer);
bool eventFilter(QObject *obj, QEvent *e);
void stickersInstalled(uint64 setId);
void queryInlineBot(UserData *bot, PeerData *peer, QString query);
void clearInlineBot();
bool overlaps(const QRect &globalRect) {
if (isHidden() || !_cache.isNull()) return false;
return QRect(st::dropdownDef.padding.left(),
st::dropdownDef.padding.top(),
_width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(),
_height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()
).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
void notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout);
void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemBeingChosen();
bool inlineResultsShown() const {
return s_inner.inlineResultsShown();
}
public slots:
void refreshStickers();
void refreshSavedGifs();
void hideStart();
void hideFinish();
void showStart();
void onWndActiveChanged();
void onTabChange();
void onScrollEmoji();
void onScrollStickers();
void onSwitch();
void onRemoveSet(quint64 setId);
void onRemoveSetSure();
void onDelayedHide();
void onRefreshIcons();
void onRefreshPanels();
void onSaveConfig();
void onSaveConfigDelayed(int32 delay);
void onInlineRequest();
void onEmptyInlineRows();
signals:
void emojiSelected(EmojiPtr emoji);
void stickerSelected(DocumentData *sticker);
void photoSelected(PhotoData *photo);
void inlineResultSelected(InlineBots::Result *result, UserData *bot);
void updateStickers();
private:
void paintStickerSettingsIcon(Painter &p) const;
void validateSelectedIcon(bool animated = false);
int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers;
bool _horizontal;
void updateContentHeight();
void leaveToChildEvent(QEvent *e, QWidget *child);
void hideAnimated();
void prepareShowHideCache();
void updateSelected();
void updateIcons();
void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab);
void updatePanelsPositions(const QVector<internal::EmojiPanel*> &panels, int32 st);
void showAll();
void hideAll();
bool _noTabUpdate;
int32 _width, _height, _bottom;
bool _hiding;
QPixmap _cache;
anim::fvalue a_opacity;
Animation _a_appearance;
QTimer _hideTimer;
BoxShadow _shadow;
FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols;
QList<internal::StickerIcon> _icons;
QVector<float64> _iconHovers;
int32 _iconOver, _iconSel, _iconDown;
bool _iconsDragging;
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _iconAnimations;
Animation _a_icons;
QPoint _iconsMousePos, _iconsMouseDown;
int32 _iconsLeft, _iconsTop;
int32 _iconsStartX, _iconsMax;
anim::ivalue _iconsX, _iconSelX;
uint64 _iconsStartAnim;
bool _stickersShown, _shownFromInlineQuery;
QPixmap _fromCache, _toCache;
anim::ivalue a_fromCoord, a_toCoord;
anim::fvalue a_fromAlpha, a_toAlpha;
Animation _a_slide;
ScrollArea e_scroll;
internal::EmojiPanInner e_inner;
QVector<internal::EmojiPanel*> e_panels;
internal::EmojiSwitchButton e_switch;
ScrollArea s_scroll;
internal::StickerPanInner s_inner;
QVector<internal::EmojiPanel*> s_panels;
internal::EmojiSwitchButton s_switch;
uint64 _removingSetId;
QTimer _saveConfigTimer;
// inline bots
typedef QMap<QString, internal::InlineCacheEntry*> InlineCache;
InlineCache _inlineCache;
QTimer _inlineRequestTimer;
void inlineBotChanged();
int32 showInlineRows(bool newResults);
bool hideOnNoInlineResults();
void recountContentMaxHeight();
bool refreshInlineRows(int32 *added = 0);
UserData *_inlineBot;
PeerData *_inlineQueryPeer = nullptr;
QString _inlineQuery, _inlineNextQuery, _inlineNextOffset;
mtpRequestId _inlineRequestId;
void inlineResultsDone(const MTPmessages_BotResults &result);
bool inlineResultsFail(const RPCError &error);
};

View file

@ -55,7 +55,7 @@ void activateBotCommand(const HistoryItem *msg, int row, int col) {
const HistoryMessageReplyMarkup::Button *button = nullptr;
if (auto markup = msg->Get<HistoryMessageReplyMarkup>()) {
if (row < markup->rows.size()) {
const auto &buttonRow(markup->rows.at(row));
auto &buttonRow = markup->rows[row];
if (col < buttonRow.size()) {
button = &buttonRow.at(col);
}
@ -63,37 +63,39 @@ void activateBotCommand(const HistoryItem *msg, int row, int col) {
}
if (!button) return;
using ButtonType = HistoryMessageReplyMarkup::Button::Type;
switch (button->type) {
case HistoryMessageReplyMarkup::Button::Default: {
case ButtonType::Default: {
// Copy string before passing it to the sending method
// because the original button can be destroyed inside.
MsgId replyTo = (msg->id > 0) ? msg->id : 0;
sendBotCommand(msg->history()->peer, msg->fromOriginal()->asUser(), QString(button->text), replyTo);
} break;
case HistoryMessageReplyMarkup::Button::Callback: {
if (MainWidget *m = main()) {
case ButtonType::Callback:
case ButtonType::Game: {
if (auto m = main()) {
m->app_sendBotCallback(button, msg, row, col);
}
} break;
case HistoryMessageReplyMarkup::Button::Url: {
case ButtonType::Url: {
auto url = QString::fromUtf8(button->data);
UrlClickHandler(url).onClick(Qt::LeftButton);
HiddenUrlClickHandler(url).onClick(Qt::LeftButton);
} break;
case HistoryMessageReplyMarkup::Button::RequestLocation: {
case ButtonType::RequestLocation: {
Ui::showLayer(new InformBox(lang(lng_bot_share_location_unavailable)));
} break;
case HistoryMessageReplyMarkup::Button::RequestPhone: {
case ButtonType::RequestPhone: {
SharePhoneConfirmBox *box = new SharePhoneConfirmBox(msg->history()->peer);
box->connect(box, SIGNAL(confirmed(PeerData*)), App::main(), SLOT(onSharePhoneWithBot(PeerData*)));
Ui::showLayer(box);
} break;
case HistoryMessageReplyMarkup::Button::SwitchInlineSame:
case HistoryMessageReplyMarkup::Button::SwitchInline: {
case ButtonType::SwitchInlineSame:
case ButtonType::SwitchInline: {
if (auto m = App::main()) {
auto getMessageBot = [msg]() -> UserData* {
if (auto bot = msg->viaBot()) {
@ -107,7 +109,7 @@ void activateBotCommand(const HistoryItem *msg, int row, int col) {
};
if (auto bot = getMessageBot()) {
auto tryFastSwitch = [bot, &button, msgId = msg->id]() -> bool {
auto samePeer = (button->type == HistoryMessageReplyMarkup::Button::SwitchInlineSame);
auto samePeer = (button->type == ButtonType::SwitchInlineSame);
if (samePeer) {
Notify::switchInlineBotButtonReceived(QString::fromUtf8(button->data), bot, msgId);
return true;

View file

@ -193,11 +193,12 @@ enum Flags {
namespace Stickers {
static const uint64 DefaultSetId = 0; // for backward compatibility
static const uint64 CustomSetId = 0xFFFFFFFFFFFFFFFFULL;
static const uint64 RecentSetId = 0xFFFFFFFFFFFFFFFEULL; // for emoji/stickers panel, should not appear in Sets
static const uint64 NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, should not appear in Sets
static const uint64 CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers
constexpr uint64 DefaultSetId = 0; // for backward compatibility
constexpr uint64 CustomSetId = 0xFFFFFFFFFFFFFFFFULL;
constexpr uint64 RecentSetId = 0xFFFFFFFFFFFFFFFEULL; // for emoji/stickers panel, should not appear in Sets
constexpr uint64 NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, should not appear in Sets
constexpr uint64 CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers
constexpr uint64 FeaturedSetId = 0xFFFFFFFFFFFFFFFBULL; // for emoji/stickers panel, should not appear in Sets
struct Set {
Set(uint64 id, uint64 access, const QString &title, const QString &shortName, int32 count, int32 hash, MTPDstickerSet::Flags flags)
: id(id)

View file

@ -674,9 +674,9 @@ void checkForSwitchInlineButton(HistoryItem *item) {
return;
}
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
for_const (const auto &row, markup->rows) {
for_const (const auto &button, row) {
if (button.type == HistoryMessageReplyMarkup::Button::SwitchInline) {
for_const (auto &row, markup->rows) {
for_const (auto &button, row) {
if (button.type == HistoryMessageReplyMarkup::Button::Type::SwitchInline) {
Notify::switchInlineBotButtonReceived(QString::fromUtf8(button.data));
return;
}
@ -2222,7 +2222,7 @@ public:
// Copy to clipboard support.
void copyToClipboard() const override {
if (auto button = getButton()) {
if (button->type == HistoryMessageReplyMarkup::Button::Url) {
if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
auto url = QString::fromUtf8(button->data);
if (!url.isEmpty()) {
QApplication::clipboard()->setText(url);
@ -2232,7 +2232,7 @@ public:
}
QString copyToClipboardContextItemText() const override {
if (auto button = getButton()) {
if (button->type == HistoryMessageReplyMarkup::Button::Url) {
if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
return lang(lng_context_copy_link);
}
}
@ -2247,7 +2247,7 @@ public:
if (auto item = App::histItemById(_itemId)) {
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (_row < markup->rows.size()) {
const HistoryMessageReplyMarkup::ButtonRow &row(markup->rows.at(_row));
auto &row = markup->rows.at(_row);
if (_col < row.size()) {
return &row.at(_col);
}
@ -2292,14 +2292,14 @@ ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s)
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
_rows.reserve(markup->rows.size());
for (int i = 0, l = markup->rows.size(); i != l; ++i) {
const HistoryMessageReplyMarkup::ButtonRow &row(markup->rows.at(i));
auto &row = markup->rows.at(i);
int s = row.size();
ButtonRow newRow(s, Button());
for (int j = 0; j != s; ++j) {
Button &button(newRow[j]);
QString str = row.at(j).text;
auto &button = newRow[j];
auto str = row.at(j).text;
button.type = row.at(j).type;
button.link.reset(new ReplyMarkupClickHandler(item, i, j));
button.link = MakeShared<ReplyMarkupClickHandler>(item, i, j);
button.text.setText(_st->textFont(), textOneLine(str), _textPlainOptions);
button.characters = str.isEmpty() ? 1 : str.size();
}
@ -2323,14 +2323,14 @@ void ReplyKeyboard::resize(int width, int height) {
auto markup = _item->Get<HistoryMessageReplyMarkup>();
float64 y = 0, buttonHeight = _rows.isEmpty() ? _st->buttonHeight() : (float64(height + _st->buttonSkip()) / _rows.size());
for (ButtonRow &row : _rows) {
for (auto &row : _rows) {
int s = row.size();
int widthForButtons = _width - ((s - 1) * _st->buttonSkip());
int widthForText = widthForButtons;
int widthOfText = 0;
int maxMinButtonWidth = 0;
for_const (const Button &button, row) {
for_const (auto &button, row) {
widthOfText += qMax(button.text.maxWidth(), 1);
int minButtonWidth = _st->minButtonWidth(button.type);
widthForText -= minButtonWidth;
@ -2368,10 +2368,10 @@ void ReplyKeyboard::resize(int width, int height) {
}
bool ReplyKeyboard::isEnoughSpace(int width, const style::botKeyboardButton &st) const {
for_const (const auto &row, _rows) {
for_const (auto &row, _rows) {
int s = row.size();
int widthLeft = width - ((s - 1) * st.margin + s * 2 * st.padding);
for_const (const auto &button, row) {
for_const (auto &button, row) {
widthLeft -= qMax(button.text.maxWidth(), 1);
if (widthLeft < 0) {
if (row.size() > 3) {
@ -2416,8 +2416,8 @@ void ReplyKeyboard::paint(Painter &p, const QRect &clip) const {
t_assert(_width > 0);
_st->startPaint(p);
for_const (const ButtonRow &row, _rows) {
for_const (const Button &button, row) {
for_const (auto &row, _rows) {
for_const (auto &button, row) {
QRect rect(button.rect);
if (rect.y() >= clip.y() + clip.height()) return;
if (rect.y() + rect.height() < clip.y()) continue;
@ -2433,8 +2433,8 @@ void ReplyKeyboard::paint(Painter &p, const QRect &clip) const {
ClickHandlerPtr ReplyKeyboard::getState(int x, int y) const {
t_assert(_width > 0);
for_const (const ButtonRow &row, _rows) {
for_const (const Button &button, row) {
for_const (auto &row, _rows) {
for_const (auto &button, row) {
QRect rect(button.rect);
// just ignore the buttons that didn't layout well
@ -2453,7 +2453,7 @@ void ReplyKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool act
bool startAnimation = false;
for (int i = 0, rows = _rows.size(); i != rows; ++i) {
const ButtonRow &row(_rows.at(i));
auto &row = _rows.at(i);
for (int j = 0, cols = row.size(); j != cols; ++j) {
if (row.at(j).link == p) {
bool startAnimation = _animations.isEmpty();
@ -2514,8 +2514,9 @@ void ReplyKeyboard::Style::paintButton(Painter &p, const ReplyKeyboard::Button &
paintButtonBg(p, rect, pressed, button.howMuchOver);
paintButtonIcon(p, rect, button.type);
if (button.type == HistoryMessageReplyMarkup::Button::Callback) {
if (const HistoryMessageReplyMarkup::Button *data = button.link->getButton()) {
if (button.type == HistoryMessageReplyMarkup::Button::Type::Callback
|| button.type == HistoryMessageReplyMarkup::Button::Type::Game) {
if (auto data = button.link->getButton()) {
if (data->requestId) {
paintButtonLoading(p, rect);
}
@ -2541,43 +2542,48 @@ void HistoryMessageReplyMarkup::createFromButtonRows(const QVector<MTPKeyboardBu
}
rows.reserve(v.size());
for_const (const auto &row, v) {
for_const (auto &row, v) {
switch (row.type()) {
case mtpc_keyboardButtonRow: {
const auto &r(row.c_keyboardButtonRow());
const auto &b(r.vbuttons.c_vector().v);
auto &r = row.c_keyboardButtonRow();
auto &b = r.vbuttons.c_vector().v;
if (!b.isEmpty()) {
ButtonRow buttonRow;
buttonRow.reserve(b.size());
for_const (const auto &button, b) {
switch (button.type()) {
case mtpc_keyboardButton: {
buttonRow.push_back({ Button::Default, qs(button.c_keyboardButton().vtext), QByteArray(), 0 });
buttonRow.push_back({ Button::Type::Default, qs(button.c_keyboardButton().vtext), QByteArray(), 0 });
} break;
case mtpc_keyboardButtonCallback: {
const auto &buttonData(button.c_keyboardButtonCallback());
buttonRow.push_back({ Button::Callback, qs(buttonData.vtext), qba(buttonData.vdata), 0 });
auto &buttonData = button.c_keyboardButtonCallback();
buttonRow.push_back({ Button::Type::Callback, qs(buttonData.vtext), qba(buttonData.vdata), 0 });
} break;
case mtpc_keyboardButtonRequestGeoLocation: {
buttonRow.push_back({ Button::RequestLocation, qs(button.c_keyboardButtonRequestGeoLocation().vtext), QByteArray(), 0 });
buttonRow.push_back({ Button::Type::RequestLocation, qs(button.c_keyboardButtonRequestGeoLocation().vtext), QByteArray(), 0 });
} break;
case mtpc_keyboardButtonRequestPhone: {
buttonRow.push_back({ Button::RequestPhone, qs(button.c_keyboardButtonRequestPhone().vtext), QByteArray(), 0 });
buttonRow.push_back({ Button::Type::RequestPhone, qs(button.c_keyboardButtonRequestPhone().vtext), QByteArray(), 0 });
} break;
case mtpc_keyboardButtonUrl: {
const auto &buttonData(button.c_keyboardButtonUrl());
buttonRow.push_back({ Button::Url, qs(buttonData.vtext), qba(buttonData.vurl), 0 });
auto &buttonData = button.c_keyboardButtonUrl();
buttonRow.push_back({ Button::Type::Url, qs(buttonData.vtext), qba(buttonData.vurl), 0 });
} break;
case mtpc_keyboardButtonSwitchInline: {
auto &buttonData = button.c_keyboardButtonSwitchInline();
auto buttonType = buttonData.is_same_peer() ? Button::SwitchInlineSame : Button::SwitchInline;
auto buttonType = buttonData.is_same_peer() ? Button::Type::SwitchInlineSame : Button::Type::SwitchInline;
buttonRow.push_back({ buttonType, qs(buttonData.vtext), qba(buttonData.vquery), 0 });
if (buttonType == Button::SwitchInline) {
if (buttonType == Button::Type::SwitchInline) {
// Optimization flag.
// Fast check on all new messages if there is a switch button to auto-click it.
flags |= MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button;
}
} break;
case mtpc_keyboardButtonGame: {
auto &buttonData = button.c_keyboardButtonGame();
auto strData = QString::number(buttonData.vgame_id.v) + ',' + qs(buttonData.vgame_title);
buttonRow.push_back({ Button::Type::Game, qs(buttonData.vtext), strData.toUtf8(), 0 });
} break;
}
}
if (!buttonRow.isEmpty()) rows.push_back(buttonRow);
@ -6614,11 +6620,11 @@ void HistoryMessage::KeyboardStyle::paintButtonIcon(Painter &p, const QRect &rec
using Button = HistoryMessageReplyMarkup::Button;
style::sprite sprite;
switch (type) {
case Button::Url: sprite = st::msgBotKbUrlIcon; break;
// case Button::RequestPhone: sprite = st::msgBotKbRequestPhoneIcon; break;
// case Button::RequestLocation: sprite = st::msgBotKbRequestLocationIcon; break;
case Button::SwitchInlineSame:
case Button::SwitchInline: sprite = st::msgBotKbSwitchPmIcon; break;
case Button::Type::Url: sprite = st::msgBotKbUrlIcon; break;
// case Button::Type::RequestPhone: sprite = st::msgBotKbRequestPhoneIcon; break;
// case Button::Type::RequestLocation: sprite = st::msgBotKbRequestLocationIcon; break;
case Button::Type::SwitchInlineSame:
case Button::Type::SwitchInline: sprite = st::msgBotKbSwitchPmIcon; break;
}
if (!sprite.isEmpty()) {
p.drawSprite(rect.x() + rect.width() - sprite.pxWidth() - st::msgBotKbIconPadding, rect.y() + st::msgBotKbIconPadding, sprite);
@ -6634,12 +6640,13 @@ int HistoryMessage::KeyboardStyle::minButtonWidth(HistoryMessageReplyMarkup::But
using Button = HistoryMessageReplyMarkup::Button;
int result = 2 * buttonPadding(), iconWidth = 0;
switch (type) {
case Button::Url: iconWidth = st::msgBotKbUrlIcon.pxWidth(); break;
//case Button::RequestPhone: iconWidth = st::msgBotKbRequestPhoneIcon.pxWidth(); break;
//case Button::RequestLocation: iconWidth = st::msgBotKbRequestLocationIcon.pxWidth(); break;
case Button::SwitchInlineSame:
case Button::SwitchInline: iconWidth = st::msgBotKbSwitchPmIcon.pxWidth(); break;
case Button::Callback: iconWidth = st::msgInvSendingImg.pxWidth(); break;
case Button::Type::Url: iconWidth = st::msgBotKbUrlIcon.pxWidth(); break;
//case Button::Type::RequestPhone: iconWidth = st::msgBotKbRequestPhoneIcon.pxWidth(); break;
//case Button::Type::RequestLocation: iconWidth = st::msgBotKbRequestLocationIcon.pxWidth(); break;
case Button::Type::SwitchInlineSame:
case Button::Type::SwitchInline: iconWidth = st::msgBotKbSwitchPmIcon.pxWidth(); break;
case Button::Type::Callback:
case Button::Type::Game: iconWidth = st::msgInvSendingImg.pxWidth(); break;
}
if (iconWidth > 0) {
result = std::max(result, 2 * iconWidth + 4 * int(st::msgBotKbIconPadding));
@ -7871,23 +7878,25 @@ HistoryMessage::~HistoryMessage() {
}
void HistoryService::setMessageByAction(const MTPmessageAction &action) {
QList<ClickHandlerPtr> links;
LangString text = lang(lng_message_empty);
QString from = textcmdLink(1, _from->name);
auto text = lang(lng_message_empty);
auto from = textcmdLink(1, _from->name);
Links links;
links.push_back(MakeShared<PeerOpenClickHandler>(_from));
switch (action.type()) {
case mtpc_messageActionChatAddUser: {
const auto &d(action.c_messageActionChatAddUser());
const auto &v(d.vusers.c_vector().v);
auto &d = action.c_messageActionChatAddUser();
auto &v = d.vusers.c_vector().v;
bool foundSelf = false;
for (int32 i = 0, l = v.size(); i < l; ++i) {
for (int i = 0, l = v.size(); i < l; ++i) {
if (v.at(i).v == MTP::authedId()) {
foundSelf = true;
break;
}
}
if (v.size() == 1) {
UserData *u = App::user(peerFromUser(v.at(0)));
auto u = App::user(peerFromUser(v.at(0)));
if (u == _from) {
text = lng_action_user_joined(lt_from, from);
} else {
@ -7897,9 +7906,9 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
} else if (v.isEmpty()) {
text = lng_action_add_user(lt_from, from, lt_user, "somebody");
} else {
for (int32 i = 0, l = v.size(); i < l; ++i) {
UserData *u = App::user(peerFromUser(v.at(i)));
QString linkText = textcmdLink(i + 2, u->name);
for (int i = 0, l = v.size(); i < l; ++i) {
auto u = App::user(peerFromUser(v.at(i)));
auto linkText = textcmdLink(i + 2, u->name);
if (i == 0) {
text = linkText;
} else if (i + 1 < l) {
@ -7919,7 +7928,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
} break;
case mtpc_messageActionChatJoinedByLink: {
const auto &d(action.c_messageActionChatJoinedByLink());
auto &d = action.c_messageActionChatJoinedByLink();
//if (true || peerFromUser(d.vinviter_id) == _from->id) {
text = lng_action_user_joined_by_link(lt_from, from);
//} else {
@ -7933,12 +7942,12 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
} break;
case mtpc_messageActionChatCreate: {
const auto &d(action.c_messageActionChatCreate());
auto &d = action.c_messageActionChatCreate();
text = lng_action_created_chat(lt_from, from, lt_title, textClean(qs(d.vtitle)));
} break;
case mtpc_messageActionChannelCreate: {
const auto &d(action.c_messageActionChannelCreate());
auto &d = action.c_messageActionChannelCreate();
if (isPost()) {
text = lng_action_created_channel(lt_title, textClean(qs(d.vtitle)));
} else {
@ -7955,18 +7964,18 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
} break;
case mtpc_messageActionChatDeleteUser: {
const auto &d(action.c_messageActionChatDeleteUser());
auto &d = action.c_messageActionChatDeleteUser();
if (peerFromUser(d.vuser_id) == _from->id) {
text = lng_action_user_left(lt_from, from);
} else {
UserData *u = App::user(peerFromUser(d.vuser_id));
auto u = App::user(peerFromUser(d.vuser_id));
links.push_back(MakeShared<PeerOpenClickHandler>(u));
text = lng_action_kick_user(lt_from, from, lt_user, textcmdLink(2, u->name));
}
} break;
case mtpc_messageActionChatEditPhoto: {
const auto &d(action.c_messageActionChatEditPhoto());
auto &d = action.c_messageActionChatEditPhoto();
if (d.vphoto.type() == mtpc_photo) {
_media.reset(new HistoryPhoto(this, history()->peer, d.vphoto.c_photo(), st::msgServicePhotoWidth));
}
@ -7974,13 +7983,13 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
} break;
case mtpc_messageActionChatEditTitle: {
const auto &d(action.c_messageActionChatEditTitle());
auto &d = action.c_messageActionChatEditTitle();
text = isPost() ? lng_action_changed_title_channel(lt_title, textClean(qs(d.vtitle))) : lng_action_changed_title(lt_from, from, lt_title, textClean(qs(d.vtitle)));
} break;
case mtpc_messageActionChatMigrateTo: {
_flags |= MTPDmessage_ClientFlag::f_is_group_migrate;
const auto &d(action.c_messageActionChatMigrateTo());
auto &d = action.c_messageActionChatMigrateTo();
if (true/*PeerData *channel = App::channelLoaded(d.vchannel_id.v)*/) {
text = lang(lng_action_group_migrate);
} else {
@ -7990,7 +7999,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
case mtpc_messageActionChannelMigrateFrom: {
_flags |= MTPDmessage_ClientFlag::f_is_group_migrate;
const auto &d(action.c_messageActionChannelMigrateFrom());
auto &d = action.c_messageActionChannelMigrateFrom();
if (true/*PeerData *chat = App::chatLoaded(d.vchat_id.v)*/) {
text = lang(lng_action_group_migrate);
} else {
@ -7999,74 +8008,63 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
} break;
case mtpc_messageActionPinMessage: {
if (updatePinnedText(&from, &text)) {
auto pinned = Get<HistoryServicePinned>();
t_assert(pinned != nullptr);
preparePinnedText(from, &text, &links);
} break;
links.push_back(pinned->lnk);
}
case mtpc_messageActionGameScore: {
prepareGameScoreText(from, &text, &links);
} break;
default: from = QString(); break;
}
textstyleSet(&st::serviceTextStyle);
_text.setText(st::msgServiceFont, text, _historySrvOptions);
textstyleRestore();
if (!from.isEmpty()) {
_text.setLink(1, MakeShared<PeerOpenClickHandler>(_from));
}
for (int32 i = 0, l = links.size(); i < l; ++i) {
_text.setLink(i + 2, links.at(i));
setServiceText(text, links);
for (int i = 0, count = links.size(); i != count; ++i) {
_text.setLink(1 + i, links.at(i));
}
}
bool HistoryService::updatePinned(bool force) {
auto pinned = Get<HistoryServicePinned>();
t_assert(pinned != nullptr);
bool HistoryService::updateDependent(bool force) {
auto dependent = GetDependentData();
t_assert(dependent != nullptr);
if (!force) {
if (!pinned->msgId || pinned->msg) {
if (!dependent->msgId || dependent->msg) {
return true;
}
}
if (!pinned->lnk) {
pinned->lnk.reset(new GoToMessageClickHandler(history()->peer->id, pinned->msgId));
if (!dependent->lnk) {
dependent->lnk.reset(new GoToMessageClickHandler(history()->peer->id, dependent->msgId));
}
bool gotDependencyItem = false;
if (!pinned->msg) {
pinned->msg = App::histItemById(channelId(), pinned->msgId);
if (pinned->msg) {
App::historyRegDependency(this, pinned->msg);
if (!dependent->msg) {
dependent->msg = App::histItemById(channelId(), dependent->msgId);
if (dependent->msg) {
App::historyRegDependency(this, dependent->msg);
gotDependencyItem = true;
}
}
if (pinned->msg) {
updatePinnedText();
if (dependent->msg) {
updateDependentText();
} else if (force) {
if (pinned->msgId > 0) {
pinned->msgId = 0;
if (dependent->msgId > 0) {
dependent->msgId = 0;
gotDependencyItem = true;
}
updatePinnedText();
updateDependentText();
}
if (force) {
if (gotDependencyItem && App::wnd()) {
App::wnd()->notifySettingGot();
}
}
return (pinned->msg || !pinned->msgId);
return (dependent->msg || !dependent->msgId);
}
bool HistoryService::updatePinnedText(const QString *pfrom, QString *ptext) {
bool HistoryService::preparePinnedText(const QString &from, QString *outText, Links *outLinks) {
bool result = false;
QString from, text;
if (pfrom) {
from = *pfrom;
} else {
from = textcmdLink(1, _from->name);
}
QString text;
ClickHandlerPtr second;
auto pinned = Get<HistoryServicePinned>();
@ -8112,21 +8110,47 @@ bool HistoryService::updatePinnedText(const QString *pfrom, QString *ptext) {
} else {
text = lng_action_pinned_media(lt_from, from, lt_media, lang(lng_deleted_message));
}
if (ptext) {
*ptext = text;
} else {
setServiceText(text);
_text.setLink(1, MakeShared<PeerOpenClickHandler>(_from));
*outText = text;
if (second) {
_text.setLink(2, second);
outLinks->push_back(second);
}
if (history()->textCachedFor == this) {
history()->textCachedFor = 0;
return result;
}
bool HistoryService::prepareGameScoreText(const QString &from, QString *outText, Links *outLinks) {
bool result = false;
QString text;
ClickHandlerPtr second;
auto gamescore = Get<HistoryServiceGameScore>();
if (gamescore && gamescore->msg) {
auto getGameTitle = [item = gamescore->msg, &second]() -> QString {
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
for (int i = 0, rowsCount = markup->rows.size(); i != rowsCount; ++i) {
auto &row = markup->rows[i];
for (int j = 0, buttonsCount = row.size(); j != buttonsCount; ++j) {
auto &button = row[j];
if (button.type == HistoryMessageReplyMarkup::Button::Type::Game) {
auto strData = QString::fromUtf8(button.data);
second = MakeShared<ReplyMarkupClickHandler>(item, i, j);
return textcmdLink(2, strData.mid(strData.indexOf(',') + 1));
}
if (App::main()) {
App::main()->dlgUpdated(history(), id);
}
App::historyUpdateDependent(this);
}
}
return lang(lng_deleted_message);
};
text = lng_action_game_score(lt_from, from, lt_score, QString::number(gamescore->score), lt_game, getGameTitle());
result = true;
} else if (gamescore && gamescore->msgId) {
text = lng_action_game_score(lt_from, from, lt_score, QString::number(gamescore->score), lt_game, lang(lng_contacts_loading));
result = true;
} else {
text = lng_action_game_score(lt_from, from, lt_score, QString::number(gamescore->score), lt_game, lang(lng_deleted_message));
}
*outText = text;
if (second) {
outLinks->push_back(second);
}
return result;
}
@ -8134,10 +8158,17 @@ bool HistoryService::updatePinnedText(const QString *pfrom, QString *ptext) {
HistoryService::HistoryService(History *history, const MTPDmessageService &msg) :
HistoryItem(history, msg.vid.v, mtpCastFlags(msg.vflags.v), ::date(msg.vdate), msg.has_from_id() ? msg.vfrom_id.v : 0) {
if (msg.has_reply_to_msg_id()) {
if (msg.vaction.type() == mtpc_messageActionPinMessage) {
UpdateComponents(HistoryServicePinned::Bit());
MsgId pinnedMsgId = Get<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v;
if (!updatePinned() && App::api()) {
App::api()->requestMessageData(history->peer->asChannel(), pinnedMsgId, std_::make_unique<HistoryDependentItemCallback>(fullId()));
} else if (msg.vaction.type() == mtpc_messageActionGameScore) {
UpdateComponents(HistoryServiceGameScore::Bit());
Get<HistoryServiceGameScore>()->score = msg.vaction.c_messageActionGameScore().vscore.v;
}
if (auto dependent = GetDependentData()) {
dependent->msgId = msg.vreply_to_msg_id.v;
if (!updateDependent() && App::api()) {
App::api()->requestMessageData(history->peer->asChannel(), dependent->msgId, std_::make_unique<HistoryDependentItemCallback>(fullId()));
}
}
}
setMessageByAction(msg.vaction);
@ -8145,7 +8176,7 @@ HistoryService::HistoryService(History *history, const MTPDmessageService &msg)
HistoryService::HistoryService(History *history, MsgId msgId, QDateTime date, const QString &msg, MTPDmessage::Flags flags, int32 from) :
HistoryItem(history, msgId, flags, date, from) {
_text.setText(st::msgServiceFont, msg, _historySrvOptions);
setServiceText(msg, Links());
}
void HistoryService::initDimensions() {
@ -8154,6 +8185,13 @@ void HistoryService::initDimensions() {
if (_media) _media->initDimensions();
}
bool HistoryService::updateDependencyItem() {
if (GetDependentData()) {
return updateDependent(true);
}
return HistoryItem::updateDependencyItem();
}
void HistoryService::countPositionAndSize(int32 &left, int32 &width) const {
left = st::msgServiceMargin.left();
int32 maxwidth = _history->width;
@ -8176,10 +8214,14 @@ QString HistoryService::inReplyText() const {
return result.trimmed().startsWith(author()->name) ? result.trimmed().mid(author()->name.size()).trimmed() : result;
}
void HistoryService::setServiceText(const QString &text) {
void HistoryService::setServiceText(const QString &text, const Links &links) {
textstyleSet(&st::serviceTextStyle);
_text.setText(st::msgServiceFont, text, _historySrvOptions);
textstyleRestore();
for (int i = 0, count = links.size(); i != count; ++i) {
_text.setLink(1 + i, links.at(i));
}
setPendingInitDimensions();
_textWidth = -1;
_textHeight = 0;
@ -8296,13 +8338,19 @@ HistoryTextState HistoryService::getState(int x, int y, HistoryStateRequest requ
if (_media) {
height -= st::msgServiceMargin.top() + _media->height();
}
QRect trect(QRect(left, st::msgServiceMargin.top(), width, height).marginsAdded(-st::msgServicePadding));
auto outer = QRect(left, st::msgServiceMargin.top(), width, height);
auto trect = outer.marginsAdded(-st::msgServicePadding);
if (trect.contains(x, y)) {
textstyleSet(&st::serviceTextStyle);
auto textRequest = request.forText();
textRequest.align = style::al_center;
result = _text.getState(x - trect.x(), y - trect.y(), trect.width(), textRequest);
textstyleRestore();
if (auto gamescore = Get<HistoryServiceGameScore>()) {
if (!result.link && outer.contains(x, y)) {
result.link = gamescore->lnk;
}
}
} else if (_media) {
result = _media->getState(x - st::msgServiceMargin.left() - (width - _media->maxWidth()) / 2, y - st::msgServiceMargin.top() - height - st::msgServiceMargin.top(), request);
}
@ -8311,9 +8359,12 @@ HistoryTextState HistoryService::getState(int x, int y, HistoryStateRequest requ
void HistoryService::applyEditionToEmpty() {
TextWithEntities textWithEntities = { QString(), EntitiesInText() };
setServiceText(QString());
setServiceText(QString(), Links());
removeMedia();
clearDependency();
UpdateComponents(0);
finishEditionToEmpty();
}
@ -8352,25 +8403,58 @@ void HistoryService::eraseFromOverview() {
}
}
bool HistoryService::updateDependentText() {
auto result = false;
auto from = textcmdLink(1, _from->name);
QString text;
Links links;
links.push_back(MakeShared<PeerOpenClickHandler>(_from));
if (Has<HistoryServicePinned>()) {
result = preparePinnedText(from, &text, &links);
} else if (Has<HistoryServiceGameScore>()) {
result = prepareGameScoreText(from, &text, &links);
} else {
return result;
}
setServiceText(text, links);
if (history()->textCachedFor == this) {
history()->textCachedFor = 0;
}
if (App::main()) {
App::main()->dlgUpdated(history(), id);
}
App::historyUpdateDependent(this);
return result;
}
void HistoryService::clearDependency() {
if (auto dependent = GetDependentData()) {
if (dependent->msg) {
App::historyUnregDependency(this, dependent->msg);
}
}
}
HistoryService::~HistoryService() {
if (auto pinned = Get<HistoryServicePinned>()) {
if (pinned->msg) {
App::historyUnregDependency(this, pinned->msg);
}
}
clearDependency();
_media.clear();
}
HistoryJoined::HistoryJoined(History *history, const QDateTime &inviteDate, UserData *inviter, MTPDmessage::Flags flags)
: HistoryService(history, clientMsgId(), inviteDate, QString(), flags) {
textstyleSet(&st::serviceTextStyle);
Links links;
auto text = ([history, inviter, &links]() {
if (peerToUser(inviter->id) == MTP::authedId()) {
_text.setText(st::msgServiceFont, lang(history->isMegagroup() ? lng_action_you_joined_group : lng_action_you_joined), _historySrvOptions);
} else {
_text.setText(st::msgServiceFont, history->isMegagroup() ? lng_action_add_you_group(lt_from, textcmdLink(1, inviter->name)) : lng_action_add_you(lt_from, textcmdLink(1, inviter->name)), _historySrvOptions);
_text.setLink(1, MakeShared<PeerOpenClickHandler>(inviter));
return lang(history->isMegagroup() ? lng_action_you_joined_group : lng_action_you_joined);
}
textstyleRestore();
links.push_back(MakeShared<PeerOpenClickHandler>(inviter));
if (history->isMegagroup()) {
return lng_action_add_you_group(lt_from, textcmdLink(1, inviter->name));
}
return lng_action_add_you(lt_from, textcmdLink(1, inviter->name));
})();
setServiceText(text, links);
}
void GoToMessageClickHandler::onClickImpl() const {

View file

@ -813,7 +813,7 @@ struct HistoryMessageReplyMarkup : public BaseComponent<HistoryMessageReplyMarku
void create(const MTPReplyMarkup &markup);
struct Button {
enum Type {
enum class Type {
Default,
Url,
Callback,
@ -821,6 +821,7 @@ struct HistoryMessageReplyMarkup : public BaseComponent<HistoryMessageReplyMarku
RequestLocation,
SwitchInline,
SwitchInlineSame,
Game,
};
Type type;
QString text;
@ -2757,12 +2758,19 @@ inline MTPDmessage::Flags newMessageFlags(PeerData *p) {
return result;
}
struct HistoryServicePinned : public BaseComponent<HistoryServicePinned> {
struct HistoryServiceDependentData {
MsgId msgId = 0;
HistoryItem *msg = nullptr;
ClickHandlerPtr lnk;
};
struct HistoryServicePinned : public BaseComponent<HistoryServicePinned>, public HistoryServiceDependentData {
};
struct HistoryServiceGameScore : public BaseComponent<HistoryServiceGameScore>, public HistoryServiceDependentData {
int score = 0;
};
namespace HistoryLayout {
class ServiceMessagePainter;
} // namespace HistoryLayout
@ -2776,18 +2784,16 @@ public:
return _create(history, msgId, date, msg, flags, from);
}
bool updateDependencyItem() override {
return updatePinned(true);
}
bool updateDependencyItem() override;
MsgId dependencyMsgId() const override {
if (const HistoryServicePinned *pinned = Get<HistoryServicePinned>()) {
return pinned->msgId;
if (auto dependent = GetDependentData()) {
return dependent->msgId;
}
return 0;
}
bool notificationReady() const override {
if (const HistoryServicePinned *pinned = Get<HistoryServicePinned>()) {
return (pinned->msg || !pinned->msgId);
if (auto dependent = GetDependentData()) {
return (dependent->msg || !dependent->msgId);
}
return true;
}
@ -2826,8 +2832,6 @@ public:
QString inDialogsText() const override;
QString inReplyText() const override;
void setServiceText(const QString &text);
~HistoryService();
protected:
@ -2840,11 +2844,31 @@ protected:
void initDimensions() override;
int resizeGetHeight_(int width) override;
using Links = QList<ClickHandlerPtr>;
void setServiceText(const QString &text, const Links &links);
void removeMedia();
private:
HistoryServiceDependentData *GetDependentData() {
if (auto pinned = Get<HistoryServicePinned>()) {
return pinned;
} else if (auto gamescore = Get<HistoryServiceGameScore>()) {
return gamescore;
}
return nullptr;
}
const HistoryServiceDependentData *GetDependentData() const {
return const_cast<HistoryService*>(this)->GetDependentData();
}
bool updateDependent(bool force = false);
bool updateDependentText();
void clearDependency();
void setMessageByAction(const MTPmessageAction &action);
bool updatePinned(bool force = false);
bool updatePinnedText(const QString *pfrom = nullptr, QString *ptext = nullptr);
bool preparePinnedText(const QString &from, QString *outText, Links *outLinks);
bool prepareGameScoreText(const QString &from, QString *outText, Links *outLinks);
};

View file

@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "boxes/confirmbox.h"
#include "boxes/photosendbox.h"
#include "boxes/sharebox.h"
#include "ui/filedialog.h"
#include "ui/toast/toast.h"
#include "ui/buttons/history_down_button.h"
@ -34,8 +35,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "history/history_service_layout.h"
#include "profile/profile_members_widget.h"
#include "core/click_handler_types.h"
#include "stickers/emoji_pan.h"
#include "lang.h"
#include "application.h"
#include "dropdown.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "passcodewidget.h"
@ -3054,11 +3057,11 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
connect(&_field, SIGNAL(linksChanged()), this, SLOT(onPreviewCheck()));
connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onWindowVisibleChanged()));
connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer()));
connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr)));
connect(&_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
connect(&_emojiPan, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*)));
connect(&_emojiPan, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*)));
connect(&_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers()));
connect(_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr)));
connect(_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
connect(_emojiPan, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*)));
connect(_emojiPan, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*)));
connect(_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers()));
connect(&_sendActionStopTimer, SIGNAL(timeout()), this, SLOT(onCancelSendAction()));
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreviewTimeout()));
if (audioCapture()) {
@ -3129,25 +3132,25 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
_silent.hide();
_cmdStart.hide();
_attachDocument.installEventFilter(&_attachType);
_attachPhoto.installEventFilter(&_attachType);
_attachEmoji.installEventFilter(&_emojiPan);
_attachDocument.installEventFilter(_attachType);
_attachPhoto.installEventFilter(_attachType);
_attachEmoji.installEventFilter(_emojiPan);
connect(&_kbShow, SIGNAL(clicked()), this, SLOT(onKbToggle()));
connect(&_kbHide, SIGNAL(clicked()), this, SLOT(onKbToggle()));
connect(&_cmdStart, SIGNAL(clicked()), this, SLOT(onCmdStart()));
connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
_attachType.hide();
_emojiPan.hide();
_attachDragDocument.hide();
_attachDragPhoto.hide();
connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
connect(_attachType->addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
_attachType->hide();
_emojiPan->hide();
_attachDragDocument->hide();
_attachDragPhoto->hide();
_topShadow.hide();
connect(&_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*)));
connect(&_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*)));
connect(_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*)));
connect(_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*)));
connect(&_updateEditTimeLeftDisplay, SIGNAL(timeout()), this, SLOT(updateField()));
@ -3156,7 +3159,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
void HistoryWidget::start() {
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
connect(App::main(), SIGNAL(savedGifsUpdated()), &_emojiPan, SLOT(refreshSavedGifs()));
connect(App::main(), SIGNAL(savedGifsUpdated()), _emojiPan, SLOT(refreshSavedGifs()));
updateRecentStickers();
if (App::main()) emit App::main()->savedGifsUpdated();
@ -3165,7 +3168,7 @@ void HistoryWidget::start() {
}
void HistoryWidget::onStickersUpdated() {
_emojiPan.refreshStickers();
_emojiPan->refreshStickers();
updateStickersByEmoji();
}
@ -3227,9 +3230,9 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
inlineBotChanged();
}
if (_inlineBot->username == cInlineGifBotUsername() && query.isEmpty()) {
_emojiPan.clearInlineBot();
_emojiPan->clearInlineBot();
} else {
_emojiPan.queryInlineBot(_inlineBot, _peer, query);
_emojiPan->queryInlineBot(_inlineBot, _peer, query);
}
if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->hideStart();
@ -3449,11 +3452,11 @@ void HistoryWidget::updateSendAction(History *history, SendActionType type, int3
}
void HistoryWidget::updateRecentStickers() {
_emojiPan.refreshStickers();
_emojiPan->refreshStickers();
}
void HistoryWidget::stickersInstalled(uint64 setId) {
_emojiPan.stickersInstalled(setId);
_emojiPan->stickersInstalled(setId);
}
void HistoryWidget::sendActionDone(const MTPBool &result, mtpRequestId req) {
@ -3527,7 +3530,8 @@ void HistoryWidget::updateStickers() {
}
if (!Global::LastRecentStickersUpdate() || now >= Global::LastRecentStickersUpdate() + StickersUpdateTimeout) {
if (!_recentStickersUpdateRequest) {
_recentStickersUpdateRequest = MTP::send(MTPmessages_GetRecentStickers(MTP_int(Local::countRecentStickersHash())), rpcDone(&HistoryWidget::recentStickersGot), rpcFail(&HistoryWidget::recentStickersFailed));
MTPmessages_GetRecentStickers::Flags flags = 0;
_recentStickersUpdateRequest = MTP::send(MTPmessages_GetRecentStickers(MTP_flags(flags), MTP_int(Local::countRecentStickersHash())), rpcDone(&HistoryWidget::recentStickersGot), rpcFail(&HistoryWidget::recentStickersFailed));
}
}
if (!Global::LastFeaturedStickersUpdate() || now >= Global::LastFeaturedStickersUpdate() + StickersUpdateTimeout) {
@ -3833,14 +3837,14 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic
_featuredStickersUpdateRequest = 0;
if (stickers.type() != mtpc_messages_featuredStickers) return;
auto &d(stickers.c_messages_featuredStickers());
auto &d = stickers.c_messages_featuredStickers();
OrderedSet<uint64> unread;
for_const (auto &unreadSetId, d.vunread.c_vector().v) {
unread.insert(unreadSetId.v);
}
auto &d_sets(d.vsets.c_vector().v);
auto &d_sets = d.vsets.c_vector().v;
auto &setsOrder = Global::RefFeaturedStickerSetsOrder();
setsOrder.clear();
@ -3851,37 +3855,53 @@ void HistoryWidget::featuredStickersGot(const MTPmessages_FeaturedStickers &stic
set.flags &= ~MTPDstickerSet_ClientFlag::f_featured; // mark for removing
}
for (int i = 0, l = d_sets.size(); i != l; ++i) {
if (d_sets.at(i).type() == mtpc_stickerSetCovered && d_sets.at(i).c_stickerSetCovered().vset.type() == mtpc_stickerSet) {
const auto &set(d_sets.at(i).c_stickerSetCovered().vset.c_stickerSet());
auto it = sets.find(set.vid.v);
QString title = stickerSetTitle(set);
auto &setData = d_sets[i];
const MTPDstickerSet *set = nullptr;
switch (setData.type()) {
case mtpc_stickerSetCovered: {
auto &d = setData.c_stickerSetCovered();
if (d.vset.type() == mtpc_stickerSet) {
set = &d.vset.c_stickerSet();
}
} break;
case mtpc_stickerSetMultiCovered: {
auto &d = setData.c_stickerSetMultiCovered();
if (d.vset.type() == mtpc_stickerSet) {
set = &d.vset.c_stickerSet();
}
} break;
}
if (set) {
auto it = sets.find(set->vid.v);
QString title = stickerSetTitle(*set);
if (it == sets.cend()) {
auto setClientFlags = MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded;
if (unread.contains(set.vid.v)) {
if (unread.contains(set->vid.v)) {
setClientFlags |= MTPDstickerSet_ClientFlag::f_unread;
}
it = sets.insert(set.vid.v, Stickers::Set(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | setClientFlags));
it = sets.insert(set->vid.v, Stickers::Set(set->vid.v, set->vaccess_hash.v, title, qs(set->vshort_name), set->vcount.v, set->vhash.v, set->vflags.v | setClientFlags));
} else {
it->access = set.vaccess_hash.v;
it->access = set->vaccess_hash.v;
it->title = title;
it->shortName = qs(set.vshort_name);
it->shortName = qs(set->vshort_name);
auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special);
it->flags = set.vflags.v | clientFlags;
it->flags = set->vflags.v | clientFlags;
it->flags |= MTPDstickerSet_ClientFlag::f_featured;
if (unread.contains(it->id)) {
it->flags |= MTPDstickerSet_ClientFlag::f_unread;
} else {
it->flags &= ~MTPDstickerSet_ClientFlag::f_unread;
}
if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) {
it->count = set.vcount.v;
it->hash = set.vhash.v;
if (it->count != set->vcount.v || it->hash != set->vhash.v || it->emoji.isEmpty()) {
it->count = set->vcount.v;
it->hash = set->vhash.v;
it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; // need to request this set
}
}
setsOrder.push_back(set.vid.v);
setsOrder.push_back(set->vid.v);
if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
setsToRequest.insert(set.vid.v, set.vaccess_hash.v);
setsToRequest.insert(set->vid.v, set->vaccess_hash.v);
}
}
}
@ -4363,11 +4383,11 @@ void HistoryWidget::updateNotifySettings() {
}
bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
return (_attachDragDocument.overlaps(globalRect) ||
_attachDragPhoto.overlaps(globalRect) ||
_attachType.overlaps(globalRect) ||
return (_attachDragDocument->overlaps(globalRect) ||
_attachDragPhoto->overlaps(globalRect) ||
_attachType->overlaps(globalRect) ||
_fieldAutocomplete->overlaps(globalRect) ||
_emojiPan.overlaps(globalRect));
_emojiPan->overlaps(globalRect));
}
void HistoryWidget::updateReportSpamStatus() {
@ -4499,8 +4519,8 @@ void HistoryWidget::updateControlsVisibility() {
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachType.hide();
_emojiPan.hide();
_attachType->hide();
_emojiPan->hide();
if (_pinnedBar) {
_pinnedBar->cancel.hide();
_pinnedBar->shadow.hide();
@ -4562,8 +4582,8 @@ void HistoryWidget::updateControlsVisibility() {
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachType.hide();
_emojiPan.hide();
_attachType->hide();
_emojiPan->hide();
if (!_field.isHidden()) {
_field.hide();
resizeEvent(0);
@ -4698,8 +4718,8 @@ void HistoryWidget::updateControlsVisibility() {
_kbShow.hide();
_kbHide.hide();
_cmdStart.hide();
_attachType.hide();
_emojiPan.hide();
_attachType->hide();
_emojiPan->hide();
_kbScroll.hide();
if (!_field.isHidden()) {
_field.hide();
@ -5240,8 +5260,8 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
onDraftSave();
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
if (replyTo < 0) cancelReply(lastKeyboardUsed);
if (_previewData && _previewData->pendingTill) previewCancel();
@ -5569,7 +5589,7 @@ void HistoryWidget::onPhotoSelect() {
_attachDocument.clearState();
_attachDocument.hide();
_attachPhoto.show();
_attachType.fastHide();
_attachType->fastHide();
if (cDefaultAttach() != dbidaPhoto) {
cSetDefaultAttach(dbidaPhoto);
@ -5597,7 +5617,7 @@ void HistoryWidget::onDocumentSelect() {
_attachPhoto.clearState();
_attachPhoto.hide();
_attachDocument.show();
_attachType.fastHide();
_attachType->fastHide();
if (cDefaultAttach() != dbidaDocument) {
cSetDefaultAttach(dbidaDocument);
@ -5634,14 +5654,14 @@ void HistoryWidget::dragEnterEvent(QDragEnterEvent *e) {
}
void HistoryWidget::dragLeaveEvent(QDragLeaveEvent *e) {
if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) {
if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) {
_attachDrag = DragStateNone;
updateDragAreas();
}
}
void HistoryWidget::leaveEvent(QEvent *e) {
if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) {
if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) {
_attachDrag = DragStateNone;
updateDragAreas();
}
@ -5689,7 +5709,7 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
_replyForwardPressed = false;
update(0, _field.y() - st::sendPadding - st::replyHeight, width(), st::replyHeight);
}
if (_attachDrag != DragStateNone || !_attachDragPhoto.isHidden() || !_attachDragDocument.isHidden()) {
if (_attachDrag != DragStateNone || !_attachDragPhoto->isHidden() || !_attachDragDocument->isHidden()) {
_attachDrag = DragStateNone;
updateDragAreas();
}
@ -5763,8 +5783,28 @@ void HistoryWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button
bool lastKeyboardUsed = (_keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard.forMsgId() == FullMsgId(_channel, msg->id));
BotCallbackInfo info = { msg->fullId(), row, col };
button->requestId = MTP::send(MTPmessages_GetBotCallbackAnswer(_peer->input, MTP_int(msg->id), MTP_bytes(button->data)), rpcDone(&HistoryWidget::botCallbackDone, info), rpcFail(&HistoryWidget::botCallbackFail, info));
auto bot = msg->viaBot();
if (!bot) {
bot = msg->from()->asUser();
if (bot && !bot->botInfo) {
bot = nullptr;
}
}
using ButtonType = HistoryMessageReplyMarkup::Button::Type;
BotCallbackInfo info = { bot, msg->fullId(), row, col, (button->type == ButtonType::Game) };
MTPmessages_GetBotCallbackAnswer::Flags flags = 0;
QByteArray sendData;
int32 sendGameId = 0;
if (info.game) {
flags = MTPmessages_GetBotCallbackAnswer::Flag::f_game_id;
auto strData = QString::fromUtf8(button->data);
sendGameId = strData.midRef(0, strData.indexOf(',')).toInt();
} else if (button->type == ButtonType::Callback) {
flags = MTPmessages_GetBotCallbackAnswer::Flag::f_data;
sendData = button->data;
}
button->requestId = MTP::send(MTPmessages_GetBotCallbackAnswer(MTP_flags(flags), _peer->input, MTP_int(msg->id), MTP_bytes(sendData), MTP_int(sendGameId)), rpcDone(&HistoryWidget::botCallbackDone, info), rpcFail(&HistoryWidget::botCallbackFail, info));
Ui::repaintHistoryItem(msg);
if (_replyToId == msg->id) {
@ -5777,7 +5817,7 @@ void HistoryWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button
}
void HistoryWidget::botCallbackDone(BotCallbackInfo info, const MTPmessages_BotCallbackAnswer &answer, mtpRequestId req) {
if (HistoryItem *item = App::histItemById(info.msgId)) {
if (auto item = App::histItemById(info.msgId)) {
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (info.row < markup->rows.size() && info.col < markup->rows.at(info.row).size()) {
if (markup->rows.at(info.row).at(info.col).requestId == req) {
@ -5788,7 +5828,7 @@ void HistoryWidget::botCallbackDone(BotCallbackInfo info, const MTPmessages_BotC
}
}
if (answer.type() == mtpc_messages_botCallbackAnswer) {
const auto &answerData(answer.c_messages_botCallbackAnswer());
auto &answerData = answer.c_messages_botCallbackAnswer();
if (answerData.has_message()) {
if (answerData.is_alert()) {
Ui::showLayer(new InformBox(qs(answerData.vmessage)));
@ -5798,7 +5838,13 @@ void HistoryWidget::botCallbackDone(BotCallbackInfo info, const MTPmessages_BotC
Ui::Toast::Show(App::wnd(), toast);
}
} else if (answerData.has_url()) {
UrlClickHandler(qs(answerData.vurl)).onClick(Qt::LeftButton);
auto url = qs(answerData.vurl);
if (info.game) {
url = appendShareGameScoreUrl(url, info.msgId);
BotGameUrlClickHandler(info.bot, url).onClick(Qt::LeftButton);
} else {
UrlClickHandler(url).onClick(Qt::LeftButton);
}
}
}
}
@ -5928,24 +5974,24 @@ void HistoryWidget::updateDragAreas() {
_field.setAcceptDrops(!_attachDrag);
switch (_attachDrag) {
case DragStateNone:
_attachDragDocument.otherLeave();
_attachDragPhoto.otherLeave();
_attachDragDocument->otherLeave();
_attachDragPhoto->otherLeave();
break;
case DragStateFiles:
_attachDragDocument.otherEnter();
_attachDragDocument.setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files));
_attachDragPhoto.fastHide();
_attachDragDocument->otherEnter();
_attachDragDocument->setText(lang(lng_drag_files_here), lang(lng_drag_to_send_files));
_attachDragPhoto->fastHide();
break;
case DragStatePhotoFiles:
_attachDragDocument.otherEnter();
_attachDragDocument.setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression));
_attachDragPhoto.otherEnter();
_attachDragPhoto.setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick));
_attachDragDocument->otherEnter();
_attachDragDocument->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_no_compression));
_attachDragPhoto->otherEnter();
_attachDragPhoto->setText(lang(lng_drag_photos_here), lang(lng_drag_to_send_quick));
break;
case DragStateImage:
_attachDragDocument.fastHide();
_attachDragPhoto.otherEnter();
_attachDragPhoto.setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick));
_attachDragDocument->fastHide();
_attachDragPhoto->otherEnter();
_attachDragPhoto->setText(lang(lng_drag_images_here), lang(lng_drag_to_send_quick));
break;
};
resizeEvent(0);
@ -6427,8 +6473,8 @@ void HistoryWidget::moveFieldControls() {
right = w;
_fieldBarCancel.move(right - _fieldBarCancel.width(), _field.y() - st::sendPadding - _fieldBarCancel.height());
_attachType.move(0, _attachDocument.y() - _attachType.height());
_emojiPan.moveBottom(_attachEmoji.y());
_attachType->move(0, _attachDocument.y() - _attachType->height());
_emojiPan->moveBottom(_attachEmoji.y());
_botStart.setGeometry(0, bottom - _botStart.height(), w, _botStart.height());
_unblock.setGeometry(0, bottom - _unblock.height(), w, _unblock.height());
@ -6458,7 +6504,7 @@ void HistoryWidget::clearInlineBot() {
inlineBotChanged();
_field.finishPlaceholder();
}
_emojiPan.clearInlineBot();
_emojiPan->clearInlineBot();
onCheckFieldAutocomplete();
}
@ -6679,7 +6725,9 @@ void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, bool silent, const M
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto caption = item->getMedia() ? item->getMedia()->getCaption() : TextWithEntities();
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedPhoto(file, MTP_string(caption.text)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
MTPDinputMediaUploadedPhoto::Flags mediaFlags = 0;
auto media = MTP_inputMediaUploadedPhoto(MTP_flags(mediaFlags), file, MTP_string(caption.text), MTPVector<MTPInputDocument>());
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), media, MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
}
}
@ -6697,7 +6745,8 @@ namespace {
if (document->type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (document->type == StickerDocument && document->sticker()) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(document->sticker()->alt), document->sticker()->set));
MTPDdocumentAttributeSticker::Flags stickerFlags = 0;
attributes.push_back(MTP_documentAttributeSticker(MTP_flags(stickerFlags), MTP_string(document->sticker()->alt), document->sticker()->set, MTPMaskCoords()));
} else if (document->type == SongDocument && document->song()) {
attributes.push_back(MTP_documentAttributeAudio(MTP_flags(MTPDdocumentAttributeAudio::Flag::f_title | MTPDdocumentAttributeAudio::Flag::f_performer), MTP_int(document->song()->duration), MTP_string(document->song()->title), MTP_string(document->song()->performer), MTPstring()));
} else if (document->type == VoiceDocument && document->voice()) {
@ -6728,7 +6777,9 @@ void HistoryWidget::onDocumentUploaded(const FullMsgId &newId, bool silent, cons
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto caption = item->getMedia() ? item->getMedia()->getCaption() : TextWithEntities();
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedDocument(file, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption.text)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
MTPDinputMediaUploadedDocument::Flags mediaFlags = 0;
auto media = MTP_inputMediaUploadedDocument(MTP_flags(mediaFlags), file, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption.text), MTPVector<MTPInputDocument>());
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), media, MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
}
}
}
@ -6754,7 +6805,9 @@ void HistoryWidget::onThumbDocumentUploaded(const FullMsgId &newId, bool silent,
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto caption = item->getMedia() ? item->getMedia()->getCaption() : TextWithEntities();
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption.text)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
MTPDinputMediaUploadedThumbDocument::Flags mediaFlags = 0;
auto media = MTP_inputMediaUploadedThumbDocument(MTP_flags(mediaFlags), file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption.text), MTPVector<MTPInputDocument>());
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), media, MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
}
}
}
@ -6925,15 +6978,15 @@ void HistoryWidget::onUpdateHistoryItems() {
}
void HistoryWidget::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) {
_emojiPan.ui_repaintInlineItem(layout);
_emojiPan->ui_repaintInlineItem(layout);
}
bool HistoryWidget::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) {
return _emojiPan.ui_isInlineItemVisible(layout);
return _emojiPan->ui_isInlineItemVisible(layout);
}
bool HistoryWidget::ui_isInlineItemBeingChosen() {
return _emojiPan.ui_isInlineItemBeingChosen();
return _emojiPan->ui_isInlineItemBeingChosen();
}
PeerData *HistoryWidget::ui_getPeerForMouseAction() {
@ -6947,7 +7000,7 @@ void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) {
}
void HistoryWidget::notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) {
_emojiPan.notify_inlineItemLayoutChanged(layout);
_emojiPan->notify_inlineItemLayoutChanged(layout);
}
void HistoryWidget::notify_handlePendingHistoryUpdate() {
@ -6982,25 +7035,25 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
_historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll.y() + _scroll.height() - _historyToEnd->height() - st::historyToDownPosition.y());
_emojiPan.setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height());
_emojiPan->setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height());
if (_membersDropdown) {
_membersDropdown->setMaxHeight(countMembersDropdownHeightMax());
}
switch (_attachDrag) {
case DragStateFiles:
_attachDragDocument.resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom());
_attachDragDocument.move(st::dragMargin.left(), st::dragMargin.top());
_attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom());
_attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top());
break;
case DragStatePhotoFiles:
_attachDragDocument.resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2);
_attachDragDocument.move(st::dragMargin.left(), st::dragMargin.top());
_attachDragPhoto.resize(_attachDragDocument.width(), _attachDragDocument.height());
_attachDragPhoto.move(st::dragMargin.left(), height() - _attachDragPhoto.height() - st::dragMargin.bottom());
_attachDragDocument->resize(width() - st::dragMargin.left() - st::dragMargin.right(), (height() - st::dragMargin.top() - st::dragMargin.bottom()) / 2);
_attachDragDocument->move(st::dragMargin.left(), st::dragMargin.top());
_attachDragPhoto->resize(_attachDragDocument->width(), _attachDragDocument->height());
_attachDragPhoto->move(st::dragMargin.left(), height() - _attachDragPhoto->height() - st::dragMargin.bottom());
break;
case DragStateImage:
_attachDragPhoto.resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom());
_attachDragPhoto.move(st::dragMargin.left(), st::dragMargin.top());
_attachDragPhoto->resize(width() - st::dragMargin.left() - st::dragMargin.right(), height() - st::dragMargin.top() - st::dragMargin.bottom());
_attachDragPhoto->move(st::dragMargin.left(), st::dragMargin.top());
break;
}
@ -7423,8 +7476,8 @@ void HistoryWidget::onFieldTabbed() {
}
}
void HistoryWidget::onStickerSend(DocumentData *sticker) {
sendExistingDocument(sticker, QString());
bool HistoryWidget::onStickerSend(DocumentData *sticker) {
return sendExistingDocument(sticker, QString());
}
void HistoryWidget::onPhotoSend(PhotoData *photo) {
@ -7497,8 +7550,8 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot
}
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
_field.setFocus();
}
@ -7567,10 +7620,10 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() {
if (_membersDropdown) {
_membersDropdown->raise();
}
_attachType.raise();
_emojiPan.raise();
_attachDragDocument.raise();
_attachDragPhoto.raise();
_attachType->raise();
_emojiPan->raise();
_attachDragDocument->raise();
_attachDragPhoto->raise();
updatePinnedBar();
result = true;
@ -7611,14 +7664,14 @@ void HistoryWidget::ReplyEditMessageDataCallback::call(ChannelData *channel, Msg
}
}
void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption) {
bool HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption) {
if (!_history || !doc || !canSendMessages(_peer)) {
return;
return false;
}
MTPInputDocument mtpInput = doc->mtpInput();
if (mtpInput.type() == mtpc_inputDocumentEmpty) {
return;
return false;
}
App::main()->readServerHistory(_history);
@ -7668,10 +7721,11 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti
}
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
_field.setFocus();
return true;
}
void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption) {
@ -7714,8 +7768,8 @@ void HistoryWidget::sendExistingPhoto(PhotoData *photo, const QString &caption)
App::historyRegRandom(randomId, newId);
if (!_fieldAutocomplete->isHidden()) _fieldAutocomplete->hideStart();
if (!_attachType.isHidden()) _attachType.hideStart();
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
if (!_attachType->isHidden()) _attachType->hideStart();
if (!_emojiPan->isHidden()) _emojiPan->hideStart();
_field.setFocus();
}

View file

@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localimageloader.h"
#include "ui/boxshadow.h"
#include "dropdown.h"
#include "history/history_common.h"
#include "history/field_autocomplete.h"
#include "window/section_widget.h"
@ -39,6 +38,10 @@ class HistoryDownButton;
class InnerDropdown;
} // namespace Ui
class Dropdown;
class DragArea;
class EmojiPan;
class HistoryWidget;
class HistoryInner : public TWidget, public AbstractTooltipShower {
Q_OBJECT
@ -800,7 +803,7 @@ public slots:
void onTextChange();
void onFieldTabbed();
void onStickerSend(DocumentData *sticker);
bool onStickerSend(DocumentData *sticker);
void onPhotoSend(PhotoData *photo);
void onInlineResultSend(InlineBots::Result *result, UserData *bot);
@ -910,7 +913,7 @@ private:
void call(ChannelData *channel, MsgId msgId) const override;
};
void sendExistingDocument(DocumentData *doc, const QString &caption);
bool sendExistingDocument(DocumentData *doc, const QString &caption);
void sendExistingPhoto(PhotoData *photo, const QString &caption);
void drawField(Painter &p, const QRect &rect);
@ -958,8 +961,10 @@ private:
void addMessagesToBack(PeerData *peer, const QVector<MTPMessage> &messages);
struct BotCallbackInfo {
UserData *bot;
FullMsgId msgId;
int row, col;
bool game;
};
void botCallbackDone(BotCallbackInfo info, const MTPmessages_BotCallbackAnswer &answer, mtpRequestId req);
bool botCallbackFail(BotCallbackInfo info, const RPCError &error, mtpRequestId req);
@ -1123,10 +1128,10 @@ private:
ChildWidget<Ui::InnerDropdown> _membersDropdown = { nullptr };
QTimer _membersDropdownShowTimer;
Dropdown _attachType;
EmojiPan _emojiPan;
ChildWidget<Dropdown> _attachType;
ChildWidget<EmojiPan> _emojiPan;
DragState _attachDrag = DragStateNone;
DragArea _attachDragDocument, _attachDragPhoto;
ChildWidget<DragArea> _attachDragDocument, _attachDragPhoto;
int32 _selCount; // < 0 - text selected, focus list, not _field

View file

@ -27,6 +27,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "mainwidget.h"
#include "ui/filedialog.h"
#include "styles/style_stickers.h"
namespace {
constexpr int kStickerPreviewEmojiLimit = 10;
} // namespace
void LayerWidget::setInnerFocus() {
auto focused = App::wnd()->focusWidget();
@ -339,6 +346,7 @@ void LayerStackWidget::activateLayer(LayerWidget *l) {
startShow();
} else {
l->show();
l->showDone();
if (App::wnd()) App::wnd()->setInnerFocus();
updateLayerBox();
}
@ -426,7 +434,8 @@ LayerStackWidget::~LayerStackWidget() {
MediaPreviewWidget::MediaPreviewWidget(QWidget *parent) : TWidget(parent)
, a_shown(0, 0)
, _a_shown(animation(this, &MediaPreviewWidget::step_shown)) {
, _a_shown(animation(this, &MediaPreviewWidget::step_shown))
, _emojiSize(EmojiSizes[EIndex + 1] / cIntRetinaFactor()) {
setAttribute(Qt::WA_TransparentForMouseEvents);
connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update()));
}
@ -435,8 +444,8 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect());
const QPixmap &draw(currentImage());
int w = draw.width() / cIntRetinaFactor(), h = draw.height() / cIntRetinaFactor();
auto &image = currentImage();
int w = image.width() / cIntRetinaFactor(), h = image.height() / cIntRetinaFactor();
if (_a_shown.animating()) {
float64 shown = a_shown.current();
p.setOpacity(shown);
@ -444,7 +453,17 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
// h = qMax(qRound(h * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(h % 2), 1);
}
p.fillRect(r, st::stickerPreviewBg);
p.drawPixmap((width() - w) / 2, (height() - h) / 2, draw);
p.drawPixmap((width() - w) / 2, (height() - h) / 2, image);
if (!_emojiList.isEmpty()) {
int emojiCount = _emojiList.size();
int emojiWidth = emojiCount * _emojiSize + (emojiCount - 1) * st::stickerEmojiSkip;
int emojiLeft = (width() - emojiWidth) / 2;
int esize = _emojiSize * cIntRetinaFactor();
for_const (auto emoji, _emojiList) {
p.drawPixmapLeft(emojiLeft, (height() - h) / 2 - _emojiSize * 2, width(), App::emojiLarge(), QRect(emoji->x * esize, emoji->y * esize, esize, esize));
emojiLeft += _emojiSize + st::stickerEmojiSkip;
}
}
}
void MediaPreviewWidget::resizeEvent(QResizeEvent *e) {
@ -472,6 +491,7 @@ void MediaPreviewWidget::showPreview(DocumentData *document) {
startShow();
_photo = nullptr;
_document = document;
fillEmojiString();
resetGifAndCache();
}
@ -510,6 +530,42 @@ void MediaPreviewWidget::hidePreview() {
resetGifAndCache();
}
void MediaPreviewWidget::fillEmojiString() {
auto getStickerEmojiList = [this](uint64 setId) {
QList<EmojiPtr> result;
auto &sets = Global::StickerSets();
auto it = sets.constFind(setId);
if (it == sets.cend()) {
return result;
}
for (auto i = it->emoji.cbegin(), e = it->emoji.cend(); i != e; ++i) {
for_const (auto document, *i) {
if (document == _document) {
result.append(i.key());
if (result.size() >= kStickerPreviewEmojiLimit) {
return result;
}
}
}
}
return result;
};
if (auto sticker = _document->sticker()) {
auto &inputSet = sticker->set;
if (inputSet.type() == mtpc_inputStickerSetID) {
_emojiList = getStickerEmojiList(inputSet.c_inputStickerSetID().vid.v);
} else {
_emojiList.clear();
if (auto emoji = emojiFromText(sticker->alt)) {
_emojiList.append(emoji);
}
}
} else {
_emojiList.clear();
}
}
void MediaPreviewWidget::resetGifAndCache() {
if (_gif) {
if (gif()) {

View file

@ -133,7 +133,6 @@ class MediaPreviewWidget : public TWidget {
Q_OBJECT
public:
MediaPreviewWidget(QWidget *parent);
void paintEvent(QPaintEvent *e);
@ -148,10 +147,10 @@ public:
~MediaPreviewWidget();
private:
QSize currentDimensions() const;
QPixmap currentImage() const;
void startShow();
void fillEmojiString();
void resetGifAndCache();
anim::fvalue a_shown;
@ -163,6 +162,9 @@ private:
return (!_gif || _gif == Media::Clip::BadReader) ? false : true;
}
int _emojiSize;
QList<EmojiPtr> _emojiList;
void clipCallback(Media::Clip::Notification notification);
enum CacheStatus {

View file

@ -386,13 +386,15 @@ void FileLoadTask::process() {
full.save(&buffer, "JPG", 77);
}
photo = MTP_photo(MTP_long(_id), MTP_long(0), MTP_int(unixtime()), MTP_vector<MTPPhotoSize>(photoSizes));
MTPDphoto::Flags photoFlags = 0;
photo = MTP_photo(MTP_flags(photoFlags), MTP_long(_id), MTP_long(0), MTP_int(unixtime()), MTP_vector<MTPPhotoSize>(photoSizes));
}
QByteArray thumbFormat = "JPG";
int32 thumbQuality = 87;
if (!animated && filemime == stickerMime && w > 0 && h > 0 && w <= StickerMaxSize && h <= StickerMaxSize && filesize < StickerInMemory) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(""), MTP_inputStickerSetEmpty()));
MTPDdocumentAttributeSticker::Flags stickerFlags = 0;
attributes.push_back(MTP_documentAttributeSticker(MTP_flags(stickerFlags), MTP_string(""), MTP_inputStickerSetEmpty(), MTPMaskCoords()));
thumbFormat = "webp";
thumbname = qsl("thumb.webp");
}

File diff suppressed because it is too large Load diff

View file

@ -22,12 +22,150 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/basic_types.h"
namespace _local_inner {
namespace Local {
class Manager : public QObject {
void start();
void finish();
void readSettings();
void writeSettings();
void writeUserSettings();
void writeMtpData();
void reset();
bool checkPasscode(const QByteArray &passcode);
void setPasscode(const QByteArray &passcode);
enum ClearManagerTask {
ClearManagerAll = 0xFFFF,
ClearManagerDownloads = 0x01,
ClearManagerStorage = 0x02,
};
struct ClearManagerData;
class ClearManager : public QObject {
Q_OBJECT
public:
public:
ClearManager();
bool addTask(int task);
bool hasTask(ClearManagerTask task);
void start();
void stop();
signals:
void succeed(int task, void *manager);
void failed(int task, void *manager);
private slots:
void onStart();
private:
~ClearManager();
ClearManagerData *data;
};
enum ReadMapState {
ReadMapFailed = 0,
ReadMapDone = 1,
ReadMapPassNeeded = 2,
};
ReadMapState readMap(const QByteArray &pass);
int32 oldMapVersion();
int32 oldSettingsVersion();
using TextWithTags = FlatTextarea::TextWithTags;
struct MessageDraft {
MessageDraft(MsgId msgId = 0, TextWithTags textWithTags = TextWithTags(), bool previewCancelled = false)
: msgId(msgId)
, textWithTags(textWithTags)
, previewCancelled(previewCancelled) {
}
MsgId msgId;
TextWithTags textWithTags;
bool previewCancelled;
};
void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const MessageDraft &editDraft);
void readDraftsWithCursors(History *h);
void writeDraftCursors(const PeerId &peer, const MessageCursor &localCursor, const MessageCursor &editCursor);
bool hasDraftCursors(const PeerId &peer);
bool hasDraft(const PeerId &peer);
void writeFileLocation(MediaKey location, const FileLocation &local);
FileLocation readFileLocation(MediaKey location, bool check = true);
void writeImage(const StorageKey &location, const ImagePtr &img);
void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true);
TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader);
int32 hasImages();
qint64 storageImagesSize();
void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true);
TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader);
bool willStickerImageLoad(const StorageKey &location);
bool copyStickerImage(const StorageKey &oldLocation, const StorageKey &newLocation);
int32 hasStickers();
qint64 storageStickersSize();
void writeAudio(const StorageKey &location, const QByteArray &data, bool overwrite = true);
TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader);
bool copyAudio(const StorageKey &oldLocation, const StorageKey &newLocation);
int32 hasAudios();
qint64 storageAudiosSize();
void writeWebFile(const QString &url, const QByteArray &data, bool overwrite = true);
TaskId startWebFileLoad(const QString &url, webFileLoader *loader);
int32 hasWebFiles();
qint64 storageWebFilesSize();
void countVoiceWaveform(DocumentData *document);
void cancelTask(TaskId id);
void writeInstalledStickers();
void writeFeaturedStickers();
void writeRecentStickers();
void writeArchivedStickers();
void readInstalledStickers();
void readFeaturedStickers();
void readRecentStickers();
void readArchivedStickers();
int32 countStickersHash(bool checkOutdatedInfo = false);
int32 countRecentStickersHash();
int32 countFeaturedStickersHash();
void writeSavedGifs();
void readSavedGifs();
int32 countSavedGifsHash();
void writeBackground(int32 id, const QImage &img);
bool readBackground();
void writeRecentHashtagsAndBots();
void readRecentHashtagsAndBots();
void addSavedPeer(PeerData *peer, const QDateTime &position);
void removeSavedPeer(PeerData *peer);
void readSavedPeers();
void writeReportSpamStatuses();
void makeBotTrusted(UserData *bot);
bool isBotTrusted(UserData *bot);
bool encrypt(const void *src, void *dst, uint32 len, const void *key128);
bool decrypt(const void *src, void *dst, uint32 len, const void *key128);
namespace internal {
class Manager : public QObject {
Q_OBJECT
public:
Manager();
@ -42,145 +180,12 @@ namespace _local_inner {
void mapWriteTimeout();
void locationsWriteTimeout();
private:
private:
QTimer _mapWriteTimer;
QTimer _locationsWriteTimer;
};
}
namespace Local {
void start();
void finish();
void readSettings();
void writeSettings();
void writeUserSettings();
void writeMtpData();
void reset();
bool checkPasscode(const QByteArray &passcode);
void setPasscode(const QByteArray &passcode);
enum ClearManagerTask {
ClearManagerAll = 0xFFFF,
ClearManagerDownloads = 0x01,
ClearManagerStorage = 0x02,
};
struct ClearManagerData;
class ClearManager : public QObject {
Q_OBJECT
public:
ClearManager();
bool addTask(int task);
bool hasTask(ClearManagerTask task);
void start();
void stop();
signals:
void succeed(int task, void *manager);
void failed(int task, void *manager);
private slots:
void onStart();
private:
~ClearManager();
ClearManagerData *data;
};
enum ReadMapState {
ReadMapFailed = 0,
ReadMapDone = 1,
ReadMapPassNeeded = 2,
};
ReadMapState readMap(const QByteArray &pass);
int32 oldMapVersion();
int32 oldSettingsVersion();
using TextWithTags = FlatTextarea::TextWithTags;
struct MessageDraft {
MessageDraft(MsgId msgId = 0, TextWithTags textWithTags = TextWithTags(), bool previewCancelled = false)
: msgId(msgId)
, textWithTags(textWithTags)
, previewCancelled(previewCancelled) {
}
MsgId msgId;
TextWithTags textWithTags;
bool previewCancelled;
};
void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const MessageDraft &editDraft);
void readDraftsWithCursors(History *h);
void writeDraftCursors(const PeerId &peer, const MessageCursor &localCursor, const MessageCursor &editCursor);
bool hasDraftCursors(const PeerId &peer);
bool hasDraft(const PeerId &peer);
void writeFileLocation(MediaKey location, const FileLocation &local);
FileLocation readFileLocation(MediaKey location, bool check = true);
void writeImage(const StorageKey &location, const ImagePtr &img);
void writeImage(const StorageKey &location, const StorageImageSaved &jpeg, bool overwrite = true);
TaskId startImageLoad(const StorageKey &location, mtpFileLoader *loader);
int32 hasImages();
qint64 storageImagesSize();
void writeStickerImage(const StorageKey &location, const QByteArray &data, bool overwrite = true);
TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader);
bool willStickerImageLoad(const StorageKey &location);
bool copyStickerImage(const StorageKey &oldLocation, const StorageKey &newLocation);
int32 hasStickers();
qint64 storageStickersSize();
void writeAudio(const StorageKey &location, const QByteArray &data, bool overwrite = true);
TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader);
bool copyAudio(const StorageKey &oldLocation, const StorageKey &newLocation);
int32 hasAudios();
qint64 storageAudiosSize();
void writeWebFile(const QString &url, const QByteArray &data, bool overwrite = true);
TaskId startWebFileLoad(const QString &url, webFileLoader *loader);
int32 hasWebFiles();
qint64 storageWebFilesSize();
void countVoiceWaveform(DocumentData *document);
void cancelTask(TaskId id);
void writeInstalledStickers();
void writeFeaturedStickers();
void writeRecentStickers();
void writeArchivedStickers();
void readInstalledStickers();
void readFeaturedStickers();
void readRecentStickers();
void readArchivedStickers();
int32 countStickersHash(bool checkOutdatedInfo = false);
int32 countRecentStickersHash();
int32 countFeaturedStickersHash();
void writeSavedGifs();
void readSavedGifs();
int32 countSavedGifsHash();
void writeBackground(int32 id, const QImage &img);
bool readBackground();
void writeRecentHashtagsAndBots();
void readRecentHashtagsAndBots();
void addSavedPeer(PeerData *peer, const QDateTime &position);
void removeSavedPeer(PeerData *peer);
void readSavedPeers();
void writeReportSpamStatuses();
};
} // namespace internal
} // namespace Local

View file

@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/section_widget.h"
#include "window/top_bar_widget.h"
#include "data/data_drafts.h"
#include "dropdown.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "dialogswidget.h"
@ -45,6 +46,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/contactsbox.h"
#include "boxes/downloadpathbox.h"
#include "boxes/confirmphonebox.h"
#include "boxes/sharebox.h"
#include "localstorage.h"
#include "shortcuts.h"
#include "media/media_audio.h"
@ -1710,6 +1712,10 @@ void MainWidget::onShareContactCancel() {
_history->cancelShareContact();
}
bool MainWidget::onSendSticker(DocumentData *document) {
return _history->onStickerSend(document);
}
void MainWidget::dialogsCancelled() {
if (_hider) {
_hider->startHide();
@ -2045,14 +2051,14 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show
}
}
if (back || (way == Ui::ShowWay::ClearStack)) {
dlgUpdated();
if (back || (way == Ui::ShowWay::ClearStack)) {
_peerInStack = nullptr;
_msgIdInStack = 0;
dlgUpdated();
} else {
saveSectionInStack();
}
dlgUpdated();
PeerData *wasActivePeer = activePeer();
@ -2191,11 +2197,8 @@ void MainWidget::saveSectionInStack() {
} else if (_wideSection) {
_stack.push_back(std_::make_unique<StackItemSection>(_wideSection->createMemento()));
} else if (_history->peer()) {
dlgUpdated();
_peerInStack = _history->peer();
_msgIdInStack = _history->msgId();
dlgUpdated();
_stack.push_back(std_::make_unique<StackItemHistory>(_peerInStack, _msgIdInStack, _history->replyReturns()));
}
}
@ -3299,33 +3302,34 @@ bool MainWidget::started() {
}
void MainWidget::openLocalUrl(const QString &url) {
QString u(url.trimmed());
if (u.size() > 8192) u = u.mid(0, 8192);
auto urlTrimmed = url.trimmed();
if (urlTrimmed.size() > 8192) urlTrimmed = urlTrimmed.mid(0, 8192);
if (!u.startsWith(qstr("tg://"), Qt::CaseInsensitive)) {
if (!urlTrimmed.startsWith(qstr("tg://"), Qt::CaseInsensitive)) {
return;
}
auto command = urlTrimmed.midRef(qstr("tg://").size());
using namespace qthelp;
auto matchOptions = RegExOption::CaseInsensitive;
if (auto joinChatMatch = regex_match(qsl("^tg://join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"), u, matchOptions)) {
if (auto joinChatMatch = regex_match(qsl("^join/?\\?invite=([a-zA-Z0-9\\.\\_\\-]+)(&|$)"), command, matchOptions)) {
joinGroupByHash(joinChatMatch->captured(1));
} else if (auto stickerSetMatch = regex_match(qsl("^tg://addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)(&|$)"), u, matchOptions)) {
} else if (auto stickerSetMatch = regex_match(qsl("^addstickers/?\\?set=([a-zA-Z0-9\\.\\_]+)(&|$)"), command, matchOptions)) {
stickersBox(MTP_inputStickerSetShortName(MTP_string(stickerSetMatch->captured(1))));
} else if (auto shareUrlMatch = regex_match(qsl("^tg://msg_url/?\\?(.+)(#|$)"), u, matchOptions)) {
} else if (auto shareUrlMatch = regex_match(qsl("^msg_url/?\\?(.+)(#|$)"), command, matchOptions)) {
auto params = url_parse_params(shareUrlMatch->captured(1), UrlParamNameTransform::ToLower);
auto url = params.value(qsl("url"));
if (!url.isEmpty()) {
shareUrlLayer(url, params.value("text"));
}
} else if (auto confirmPhoneMatch = regex_match(qsl("^tg://confirmphone/?\\?(.+)(#|$)"), u, matchOptions)) {
} else if (auto confirmPhoneMatch = regex_match(qsl("^confirmphone/?\\?(.+)(#|$)"), command, matchOptions)) {
auto params = url_parse_params(confirmPhoneMatch->captured(1), UrlParamNameTransform::ToLower);
auto phone = params.value(qsl("phone"));
auto hash = params.value(qsl("hash"));
if (!phone.isEmpty() && !hash.isEmpty()) {
ConfirmPhoneBox::start(phone, hash);
}
} else if (auto usernameMatch = regex_match(qsl("^tg://resolve/?\\?(.+)(#|$)"), u, matchOptions)) {
} else if (auto usernameMatch = regex_match(qsl("^resolve/?\\?(.+)(#|$)"), command, matchOptions)) {
auto params = url_parse_params(usernameMatch->captured(1), UrlParamNameTransform::ToLower);
auto domain = params.value(qsl("domain"));
if (auto domainMatch = regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) {
@ -3345,6 +3349,9 @@ void MainWidget::openLocalUrl(const QString &url) {
}
openPeerByName(domain, post, startToken);
}
} else if (auto shareGameScoreMatch = regex_match(qsl("^share_game_score/?\\?(.+)(#|$)"), command, matchOptions)) {
auto params = url_parse_params(shareGameScoreMatch->captured(1), UrlParamNameTransform::ToLower);
shareGameScoreByHash(params.value(qsl("hash")));
}
}
@ -4673,7 +4680,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
auto &set = d.vstickerset.c_messages_stickerSet();
if (set.vset.type() == mtpc_stickerSet) {
auto &s = set.vset.c_stickerSet();
if (!s.is_masks()) {
auto &sets = Global::RefStickerSets();
auto it = sets.find(s.vid.v);
if (it == sets.cend()) {
@ -4685,27 +4692,30 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
writeArchived = true;
}
}
const auto &v(set.vdocuments.c_vector().v);
auto inputSet = MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access));
auto &v = set.vdocuments.c_vector().v;
it->stickers.clear();
it->stickers.reserve(v.size());
for (int32 i = 0, l = v.size(); i < l; ++i) {
DocumentData *doc = App::feedDocument(v.at(i));
for (int i = 0, l = v.size(); i < l; ++i) {
auto doc = App::feedDocument(v.at(i));
if (!doc || !doc->sticker()) continue;
it->stickers.push_back(doc);
if (doc->sticker()->set.type() != mtpc_inputStickerSetID) {
doc->sticker()->set = inputSet;
}
}
it->emoji.clear();
auto &packs = set.vpacks.c_vector().v;
for (int32 i = 0, l = packs.size(); i < l; ++i) {
for (int i = 0, l = packs.size(); i < l; ++i) {
if (packs.at(i).type() != mtpc_stickerPack) continue;
auto &pack = packs.at(i).c_stickerPack();
if (EmojiPtr e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
if (auto e = emojiGetNoColor(emojiFromText(qs(pack.vemoticon)))) {
auto &stickers = pack.vdocuments.c_vector().v;
StickerPack p;
p.reserve(stickers.size());
for (int32 j = 0, c = stickers.size(); j < c; ++j) {
DocumentData *doc = App::document(stickers.at(j).v);
for (int j = 0, c = stickers.size(); j < c; ++j) {
auto doc = App::document(stickers.at(j).v);
if (!doc || !doc->sticker()) continue;
p.push_back(doc);
@ -4738,14 +4748,16 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
emit stickersUpdated();
}
}
}
} break;
case mtpc_updateStickerSetsOrder: {
auto &d = update.c_updateStickerSetsOrder();
if (!d.is_masks()) {
auto &order = d.vorder.c_vector().v;
auto &sets = Global::StickerSets();
Stickers::Order result;
for (int32 i = 0, l = order.size(); i < l; ++i) {
for (int i = 0, l = order.size(); i < l; ++i) {
if (sets.constFind(order.at(i).v) == sets.cend()) {
break;
}
@ -4759,6 +4771,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
Local::writeInstalledStickers();
emit stickersUpdated();
}
}
} break;
case mtpc_updateStickerSets: {
@ -4772,16 +4785,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
} break;
case mtpc_updateReadFeaturedStickers: {
for (auto &set : Global::RefStickerSets()) {
if (set.flags & MTPDstickerSet_ClientFlag::f_unread) {
set.flags &= ~MTPDstickerSet_ClientFlag::f_unread;
}
}
if (Global::FeaturedStickerSetsUnreadCount()) {
Global::SetFeaturedStickerSetsUnreadCount(0);
Local::writeFeaturedStickers();
emit stickersUpdated();
}
// We read some of the featured stickers, perhaps not all of them.
// Here we don't know what featured sticker sets were read, so we
// request all of them once again.
Global::SetLastFeaturedStickersUpdate(0);
App::main()->updateStickers();
} break;
////// Cloud saved GIFs

View file

@ -212,6 +212,7 @@ public:
void onSendFileCancel(const FileLoadResultPtr &file);
void onShareContactConfirm(const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, bool ctrlShiftEnter);
void onShareContactCancel();
bool onSendSticker(DocumentData *sticker);
void destroyData();
void updateOnlineDisplayIn(int32 msecs);

View file

@ -146,12 +146,12 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#f7aff1c0 file:InputFile caption:string = InputMedia;
inputMediaUploadedPhoto#630c9af1 flags:# file:InputFile caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaPhoto#e9bfb4f3 id:InputPhoto caption:string = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
inputMediaUploadedDocument#1d89306d file:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;
inputMediaUploadedThumbDocument#ad613491 file:InputFile thumb:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;
inputMediaUploadedDocument#d070f1e9 flags:# file:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaUploadedThumbDocument#50d88cae flags:# file:InputFile thumb:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaDocument#1a77f29c id:InputDocument caption:string = InputMedia;
inputMediaVenue#2827a81a geo_point:InputGeoPoint title:string address:string provider:string venue_id:string = InputMedia;
inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
@ -159,8 +159,8 @@ inputMediaPhotoExternal#b55f4f18 url:string caption:string = InputMedia;
inputMediaDocumentExternal#e5e9607c url:string caption:string = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#94254732 file:InputFile crop:InputPhotoCrop = InputChatPhoto;
inputChatPhoto#b2e1bf08 id:InputPhoto crop:InputPhotoCrop = InputChatPhoto;
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
@ -172,9 +172,6 @@ inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLo
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation;
inputPhotoCropAuto#ade6b004 = InputPhotoCrop;
inputPhotoCrop#d9915325 crop_left:double crop_top:double crop_width:double = InputPhotoCrop;
inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
peerUser#9db1bc6d user_id:int = Peer;
@ -253,11 +250,12 @@ messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction;
messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction;
messageActionPinMessage#94bd38ed = MessageAction;
messageActionHistoryClear#9fbab604 = MessageAction;
messageActionGameScore#3a14cfa5 game_id:int score:int = MessageAction;
dialog#66ffba14 flags:# peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
photoEmpty#2331b22d id:long = Photo;
photo#cded42fe id:long access_hash:long date:int sizes:Vector<PhotoSize> = Photo;
photo#9288dd29 flags:# has_stickers:flags.0?true id:long access_hash:long date:int sizes:Vector<PhotoSize> = Photo;
photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
@ -382,16 +380,16 @@ updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update;
updateChatAdmins#6e947941 chat_id:int enabled:Bool version:int = Update;
updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#f0dfb451 order:Vector<long> = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector<long> = Update;
updateStickerSets#43ae3dec = Update;
updateSavedGifs#9375341e = Update;
updateBotInlineQuery#54826690 flags:# query_id:long user_id:int query:string geo:flags.0?GeoPoint offset:string = Update;
updateBotInlineSend#e48f964 flags:# user_id:int query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
updateEditChannelMessage#1b3f4df7 message:Message pts:int pts_count:int = Update;
updateChannelPinnedMessage#98592475 channel_id:int id:int = Update;
updateBotCallbackQuery#a68c688c query_id:long user_id:int peer:Peer msg_id:int data:bytes = Update;
updateBotCallbackQuery#81c5615f flags:# query_id:long user_id:int peer:Peer msg_id:int data:flags.0?bytes game_id:flags.1?int = Update;
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
updateInlineBotCallbackQuery#2cbd95af query_id:long user_id:int msg_id:InputBotInlineMessageID data:bytes = Update;
updateInlineBotCallbackQuery#d618a28b flags:# query_id:long user_id:int msg_id:InputBotInlineMessageID data:flags.0?bytes game_id:flags.1?int = Update;
updateReadChannelOutbox#25d6c9c7 channel_id:int max_id:int = Update;
updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
updateReadFeaturedStickers#571d2742 = Update;
@ -508,10 +506,11 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
messages.stickersNotModified#f1749a22 = messages.Stickers;
messages.stickers#8a8ecd32 hash:string stickers:Vector<Document> = messages.Stickers;
@ -559,7 +558,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
stickerSet#cd303b41 flags:# installed:flags.0?true archived:flags.1?true official:flags.2?true id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet;
stickerSet#cd303b41 flags:# installed:flags.0?true archived:flags.1?true official:flags.2?true masks:flags.3?true id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
@ -573,6 +572,7 @@ keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton;
keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
keyboardButtonGame#28fc3164 text:string game_title:string game_id:int start_param:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
@ -714,6 +714,12 @@ messages.stickerSetInstallResultSuccess#38641628 = messages.StickerSetInstallRes
messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered> = messages.StickerSetInstallResult;
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia;
inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia;
---functions---
@ -739,6 +745,7 @@ auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
account.registerDevice#637ea878 token_type:int token:string = Bool;
account.unregisterDevice#65c55b40 token_type:int token:string = Bool;
@ -796,7 +803,7 @@ messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.sendMedia#c8f16791 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long reply_markup:flags.2?ReplyMarkup = Updates;
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.hideReportSpam#a8f1709b peer:InputPeer = Bool;
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
@ -819,7 +826,6 @@ messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:
messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
messages.getStickers#ae22e045 emoticon:string hash:string = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#25223e24 message:string = MessageMedia;
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite;
@ -834,7 +840,7 @@ messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates;
messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#15a3b8e3 chat_id:int = Updates;
messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.reorderStickerSets#9fcfbc30 order:Vector<long> = Bool;
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
@ -845,25 +851,28 @@ messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:fla
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
messages.editMessage#ce91e4ca flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.editInlineBotMessage#130c2c85 flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
messages.getBotCallbackAnswer#a6e94f04 peer:InputPeer msg_id:int data:bytes = messages.BotCallbackAnswer;
messages.getBotCallbackAnswer#6c996518 flags:# peer:InputPeer msg_id:int data:flags.0?bytes game_id:flags.1?int = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#c927d44b flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string = Bool;
messages.getPeerDialogs#2d9776b9 peers:Vector<InputPeer> = messages.PeerDialogs;
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
messages.getAllDrafts#6a3f8d65 = Updates;
messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers;
messages.readFeaturedStickers#e21cbb = Bool;
messages.getRecentStickers#99197c2c hash:int = messages.RecentStickers;
messages.saveRecentSticker#348e39bf id:InputDocument unsave:Bool = Bool;
messages.clearRecentStickers#ab02e5d2 = Bool;
messages.getUnusedStickers#4309d65b limit:int = Vector<StickerSetCovered>;
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
messages.getRecentStickers#5ea192c9 flags:# attached:flags.0?true hash:int = messages.RecentStickers;
messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool;
messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool;
messages.getArchivedStickers#906e241f offset_id:long limit:int = messages.ArchivedStickers;
messages.setGameScore#dfbc7c1f flags:# edit_message:flags.0?true peer:InputPeer id:int user_id:InputUser game_id:int score:int = Updates;
messages.setInlineGameScore#54f882f1 flags:# edit_message:flags.0?true id:InputBotInlineMessageID user_id:InputUser game_id:int score:int = Bool;
messages.getMaskStickers#65b8c79f hash:int = messages.AllStickers;
messages.getAttachedStickers#cc5b67cc media:InputStickeredMedia = Vector<StickerSetCovered>;
updates.getState#edd4882a = updates.State;
updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference;
updates.getChannelDifference#bb32d7c0 channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
photos.updateProfilePhoto#eef579a0 id:InputPhoto crop:InputPhotoCrop = UserProfilePhoto;
photos.uploadProfilePhoto#d50f9c88 file:InputFile caption:string geo_point:InputGeoPoint crop:InputPhotoCrop = photos.Photo;
photos.updateProfilePhoto#f0bb5152 id:InputPhoto = UserProfilePhoto;
photos.uploadProfilePhoto#4f32c098 file:InputFile = photos.Photo;
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
@ -908,4 +917,4 @@ channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;
// LAYER 55
// LAYER 56

View file

@ -588,6 +588,8 @@ void _serialize_inputMediaEmpty(MTPStringLogger &to, int32 stage, int32 lev, Typ
}
void _serialize_inputMediaUploadedPhoto(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDinputMediaUploadedPhoto::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -595,8 +597,10 @@ void _serialize_inputMediaUploadedPhoto(MTPStringLogger &to, int32 stage, int32
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" caption: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" caption: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" stickers: "); ++stages.back(); if (flag & MTPDinputMediaUploadedPhoto::Flag::f_stickers) { types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -644,6 +648,8 @@ void _serialize_inputMediaContact(MTPStringLogger &to, int32 stage, int32 lev, T
}
void _serialize_inputMediaUploadedDocument(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDinputMediaUploadedDocument::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -651,15 +657,19 @@ void _serialize_inputMediaUploadedDocument(MTPStringLogger &to, int32 stage, int
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" mime_type: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" attributes: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" caption: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" mime_type: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" attributes: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" caption: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" stickers: "); ++stages.back(); if (flag & MTPDinputMediaUploadedDocument::Flag::f_stickers) { types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_inputMediaUploadedThumbDocument(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDinputMediaUploadedThumbDocument::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -667,11 +677,13 @@ void _serialize_inputMediaUploadedThumbDocument(MTPStringLogger &to, int32 stage
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" thumb: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" mime_type: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" attributes: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" caption: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" thumb: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" mime_type: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" attributes: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" caption: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 6: to.add(" stickers: "); ++stages.back(); if (flag & MTPDinputMediaUploadedThumbDocument::Flag::f_stickers) { types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -762,7 +774,6 @@ void _serialize_inputChatUploadedPhoto(MTPStringLogger &to, int32 stage, int32 l
}
switch (stage) {
case 0: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" crop: "); ++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;
}
}
@ -776,7 +787,6 @@ void _serialize_inputChatPhoto(MTPStringLogger &to, int32 stage, int32 lev, Type
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" crop: "); ++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;
}
}
@ -861,25 +871,6 @@ void _serialize_inputDocumentFileLocation(MTPStringLogger &to, int32 stage, int3
}
}
void _serialize_inputPhotoCropAuto(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ inputPhotoCropAuto }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_inputPhotoCrop(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ inputPhotoCrop");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" crop_left: "); ++stages.back(); types.push_back(mtpc_double+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" crop_top: "); ++stages.back(); types.push_back(mtpc_double+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" crop_width: "); ++stages.back(); types.push_back(mtpc_double+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_inputAppEvent(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
@ -1672,6 +1663,20 @@ void _serialize_messageActionHistoryClear(MTPStringLogger &to, int32 stage, int3
to.add("{ messageActionHistoryClear }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_messageActionGameScore(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messageActionGameScore");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" game_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" score: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_dialog(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDdialog::Flags flag(iflag);
@ -1709,6 +1714,8 @@ void _serialize_photoEmpty(MTPStringLogger &to, int32 stage, int32 lev, Types &t
}
void _serialize_photo(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDphoto::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -1716,10 +1723,12 @@ void _serialize_photo(MTPStringLogger &to, int32 stage, int32 lev, Types &types,
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_long+0); 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+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" sizes: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" has_stickers: "); ++stages.back(); if (flag & MTPDphoto::Flag::f_has_stickers) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" id: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" date: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" sizes: "); ++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;
}
}
@ -2880,6 +2889,8 @@ void _serialize_updateNewStickerSet(MTPStringLogger &to, int32 stage, int32 lev,
}
void _serialize_updateStickerSetsOrder(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDupdateStickerSetsOrder::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -2887,7 +2898,9 @@ void _serialize_updateStickerSetsOrder(MTPStringLogger &to, int32 stage, int32 l
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" order: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" masks: "); ++stages.back(); if (flag & MTPDupdateStickerSetsOrder::Flag::f_masks) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" order: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+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;
}
}
@ -2970,6 +2983,8 @@ void _serialize_updateChannelPinnedMessage(MTPStringLogger &to, int32 stage, int
}
void _serialize_updateBotCallbackQuery(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDupdateBotCallbackQuery::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -2977,11 +2992,13 @@ void _serialize_updateBotCallbackQuery(MTPStringLogger &to, int32 stage, int32 l
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" query_id: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" user_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" msg_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" data: "); ++stages.back(); types.push_back(mtpc_bytes+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" query_id: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" user_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" msg_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" data: "); ++stages.back(); if (flag & MTPDupdateBotCallbackQuery::Flag::f_data) { types.push_back(mtpc_bytes+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 6: to.add(" game_id: "); ++stages.back(); if (flag & MTPDupdateBotCallbackQuery::Flag::f_game_id) { types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -3002,6 +3019,8 @@ void _serialize_updateEditMessage(MTPStringLogger &to, int32 stage, int32 lev, T
}
void _serialize_updateInlineBotCallbackQuery(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDupdateInlineBotCallbackQuery::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -3009,10 +3028,12 @@ void _serialize_updateInlineBotCallbackQuery(MTPStringLogger &to, int32 stage, i
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" query_id: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" user_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" msg_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" data: "); ++stages.back(); types.push_back(mtpc_bytes+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" query_id: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" user_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" msg_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" data: "); ++stages.back(); if (flag & MTPDupdateInlineBotCallbackQuery::Flag::f_data) { types.push_back(mtpc_bytes+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 5: to.add(" game_id: "); ++stages.back(); if (flag & MTPDupdateInlineBotCallbackQuery::Flag::f_game_id) { types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -4008,6 +4029,8 @@ void _serialize_documentAttributeAnimated(MTPStringLogger &to, int32 stage, int3
}
void _serialize_documentAttributeSticker(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPDdocumentAttributeSticker::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -4015,8 +4038,11 @@ void _serialize_documentAttributeSticker(MTPStringLogger &to, int32 stage, int32
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" alt: "); ++stages.back(); types.push_back(mtpc_string+0); 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;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" mask: "); ++stages.back(); if (flag & MTPDdocumentAttributeSticker::Flag::f_mask) { to.add("YES [ BY BIT 1 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break;
case 2: to.add(" alt: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" stickerset: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" mask_coords: "); ++stages.back(); if (flag & MTPDdocumentAttributeSticker::Flag::f_mask_coords) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -4069,6 +4095,10 @@ void _serialize_documentAttributeFilename(MTPStringLogger &to, int32 stage, int3
}
}
void _serialize_documentAttributeHasStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ documentAttributeHasStickers }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_messages_stickersNotModified(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ messages_stickersNotModified }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
@ -4446,12 +4476,13 @@ void _serialize_stickerSet(MTPStringLogger &to, int32 stage, int32 lev, Types &t
case 1: to.add(" installed: "); ++stages.back(); if (flag & MTPDstickerSet::Flag::f_installed) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" archived: "); ++stages.back(); if (flag & MTPDstickerSet::Flag::f_archived) { to.add("YES [ BY BIT 1 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break;
case 3: to.add(" official: "); ++stages.back(); if (flag & MTPDstickerSet::Flag::f_official) { to.add("YES [ BY BIT 2 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 2 IN FIELD flags ]"); } break;
case 4: to.add(" id: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 6: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 7: to.add(" short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 8: to.add(" count: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 9: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" masks: "); ++stages.back(); if (flag & MTPDstickerSet::Flag::f_masks) { to.add("YES [ BY BIT 3 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 3 IN FIELD flags ]"); } break;
case 5: to.add(" id: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 6: to.add(" access_hash: "); ++stages.back(); types.push_back(mtpc_long+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 7: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 8: to.add(" short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 9: to.add(" count: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 10: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int+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;
}
}
@ -4585,6 +4616,22 @@ void _serialize_keyboardButtonSwitchInline(MTPStringLogger &to, int32 stage, int
}
}
void _serialize_keyboardButtonGame(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ keyboardButtonGame");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" text: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" game_title: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" game_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" start_param: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_keyboardButtonRow(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
@ -5891,6 +5938,62 @@ void _serialize_stickerSetCovered(MTPStringLogger &to, int32 stage, int32 lev, T
}
}
void _serialize_stickerSetMultiCovered(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ stickerSetMultiCovered");
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(" covers: "); ++stages.back(); types.push_back(00); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_maskCoords(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ maskCoords");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" n: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" x: "); ++stages.back(); types.push_back(mtpc_double+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" y: "); ++stages.back(); types.push_back(mtpc_double+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" zoom: "); ++stages.back(); types.push_back(mtpc_double+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_inputStickeredMediaPhoto(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ inputStickeredMediaPhoto");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_inputStickeredMediaDocument(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ inputStickeredMediaDocument");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_req_pq(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
@ -6072,6 +6175,19 @@ void _serialize_auth_cancelCode(MTPStringLogger &to, int32 stage, int32 lev, Typ
}
}
void _serialize_auth_dropTempAuthKeys(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ auth_dropTempAuthKeys");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" except_auth_keys: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_account_registerDevice(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
@ -6401,6 +6517,8 @@ void _serialize_messages_editChatAdmin(MTPStringLogger &to, int32 stage, int32 l
}
void _serialize_messages_reorderStickerSets(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPmessages_reorderStickerSets::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -6408,7 +6526,9 @@ void _serialize_messages_reorderStickerSets(MTPStringLogger &to, int32 stage, in
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" order: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" masks: "); ++stages.back(); if (flag & MTPmessages_reorderStickerSets::Flag::f_masks) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" order: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+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;
}
}
@ -6509,10 +6629,21 @@ void _serialize_messages_saveDraft(MTPStringLogger &to, int32 stage, int32 lev,
}
void _serialize_messages_readFeaturedStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ messages_readFeaturedStickers }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_readFeaturedStickers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_saveRecentSticker(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPmessages_saveRecentSticker::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -6520,14 +6651,48 @@ void _serialize_messages_saveRecentSticker(MTPStringLogger &to, int32 stage, int
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" unsave: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" attached: "); ++stages.back(); if (flag & MTPmessages_saveRecentSticker::Flag::f_attached) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" unsave: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_clearRecentStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ messages_clearRecentStickers }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
MTPmessages_clearRecentStickers::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_clearRecentStickers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" attached: "); ++stages.back(); if (flag & MTPmessages_clearRecentStickers::Flag::f_attached) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_setInlineGameScore(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPmessages_setInlineGameScore::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_setInlineGameScore");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" edit_message: "); ++stages.back(); if (flag & MTPmessages_setInlineGameScore::Flag::f_edit_message) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" user_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" game_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" score: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_upload_saveFilePart(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
@ -7447,10 +7612,11 @@ void _serialize_messages_forwardMessages(MTPStringLogger &to, int32 stage, int32
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" silent: "); ++stages.back(); if (flag & MTPmessages_forwardMessages::Flag::f_silent) { to.add("YES [ BY BIT 5 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 5 IN FIELD flags ]"); } break;
case 2: to.add(" background: "); ++stages.back(); if (flag & MTPmessages_forwardMessages::Flag::f_background) { to.add("YES [ BY BIT 6 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break;
case 3: to.add(" from_peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_int+0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" random_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+0); stages.push_back(0); flags.push_back(0); break;
case 6: to.add(" to_peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" with_my_score: "); ++stages.back(); if (flag & MTPmessages_forwardMessages::Flag::f_with_my_score) { to.add("YES [ BY BIT 8 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 8 IN FIELD flags ]"); } break;
case 4: to.add(" from_peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_int+0); stages.push_back(0); flags.push_back(0); break;
case 6: to.add(" random_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(mtpc_long+0); stages.push_back(0); flags.push_back(0); break;
case 7: to.add(" to_peer: "); ++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;
}
}
@ -7645,6 +7811,27 @@ void _serialize_messages_getAllDrafts(MTPStringLogger &to, int32 stage, int32 le
to.add("{ messages_getAllDrafts }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
void _serialize_messages_setGameScore(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPmessages_setGameScore::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_setGameScore");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" edit_message: "); ++stages.back(); if (flag & MTPmessages_setGameScore::Flag::f_edit_message) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" user_id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" game_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 6: to.add(" score: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_channels_createChannel(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPchannels_createChannel::Flags flag(iflag);
@ -8006,20 +8193,6 @@ void _serialize_photos_deletePhotos(MTPStringLogger &to, int32 stage, int32 lev,
}
}
void _serialize_messages_getStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_getStickers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" emoticon: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_getAllStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
@ -8033,6 +8206,19 @@ void _serialize_messages_getAllStickers(MTPStringLogger &to, int32 stage, int32
}
}
void _serialize_messages_getMaskStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_getMaskStickers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_getWebPagePreview(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
@ -8189,6 +8375,8 @@ void _serialize_messages_getMessageEditData(MTPStringLogger &to, int32 stage, in
}
void _serialize_messages_getBotCallbackAnswer(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPmessages_getBotCallbackAnswer::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -8196,9 +8384,11 @@ void _serialize_messages_getBotCallbackAnswer(MTPStringLogger &to, int32 stage,
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" msg_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" data: "); ++stages.back(); types.push_back(mtpc_bytes+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" peer: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" msg_id: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" data: "); ++stages.back(); if (flag & MTPmessages_getBotCallbackAnswer::Flag::f_data) { types.push_back(mtpc_bytes+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 4: to.add(" game_id: "); ++stages.back(); if (flag & MTPmessages_getBotCallbackAnswer::Flag::f_game_id) { types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 1 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -8230,6 +8420,8 @@ void _serialize_messages_getFeaturedStickers(MTPStringLogger &to, int32 stage, i
}
void _serialize_messages_getRecentStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
MTPmessages_getRecentStickers::Flags flag(iflag);
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
@ -8237,20 +8429,9 @@ void _serialize_messages_getRecentStickers(MTPStringLogger &to, int32 stage, int
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_messages_getUnusedStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_getUnusedStickers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" limit: "); ++stages.back(); types.push_back(mtpc_int+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" attached: "); ++stages.back(); if (flag & MTPmessages_getRecentStickers::Flag::f_attached) { to.add("YES [ BY BIT 0 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 2: to.add(" hash: "); ++stages.back(); types.push_back(mtpc_int+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;
}
}
@ -8269,6 +8450,19 @@ void _serialize_messages_getArchivedStickers(MTPStringLogger &to, int32 stage, i
}
}
void _serialize_messages_getAttachedStickers(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
if (stage) {
to.add(",\n").addSpaces(lev);
} else {
to.add("{ messages_getAttachedStickers");
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" media: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
void _serialize_updates_getState(MTPStringLogger &to, int32 stage, int32 lev, Types &types, Types &vtypes, StagesFlags &stages, StagesFlags &flags, const mtpPrime *start, const mtpPrime *end, int32 iflag) {
to.add("{ updates_getState }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();
}
@ -8313,7 +8507,6 @@ void _serialize_photos_updateProfilePhoto(MTPStringLogger &to, int32 stage, int3
}
switch (stage) {
case 0: to.add(" id: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" crop: "); ++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;
}
}
@ -8327,9 +8520,6 @@ void _serialize_photos_uploadProfilePhoto(MTPStringLogger &to, int32 stage, int3
}
switch (stage) {
case 0: to.add(" file: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" caption: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" geo_point: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" crop: "); ++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;
}
}
@ -8553,8 +8743,6 @@ namespace {
_serializers.insert(mtpc_inputFileLocation, _serialize_inputFileLocation);
_serializers.insert(mtpc_inputEncryptedFileLocation, _serialize_inputEncryptedFileLocation);
_serializers.insert(mtpc_inputDocumentFileLocation, _serialize_inputDocumentFileLocation);
_serializers.insert(mtpc_inputPhotoCropAuto, _serialize_inputPhotoCropAuto);
_serializers.insert(mtpc_inputPhotoCrop, _serialize_inputPhotoCrop);
_serializers.insert(mtpc_inputAppEvent, _serialize_inputAppEvent);
_serializers.insert(mtpc_peerUser, _serialize_peerUser);
_serializers.insert(mtpc_peerChat, _serialize_peerChat);
@ -8619,6 +8807,7 @@ namespace {
_serializers.insert(mtpc_messageActionChannelMigrateFrom, _serialize_messageActionChannelMigrateFrom);
_serializers.insert(mtpc_messageActionPinMessage, _serialize_messageActionPinMessage);
_serializers.insert(mtpc_messageActionHistoryClear, _serialize_messageActionHistoryClear);
_serializers.insert(mtpc_messageActionGameScore, _serialize_messageActionGameScore);
_serializers.insert(mtpc_dialog, _serialize_dialog);
_serializers.insert(mtpc_photoEmpty, _serialize_photoEmpty);
_serializers.insert(mtpc_photo, _serialize_photo);
@ -8815,6 +9004,7 @@ namespace {
_serializers.insert(mtpc_documentAttributeVideo, _serialize_documentAttributeVideo);
_serializers.insert(mtpc_documentAttributeAudio, _serialize_documentAttributeAudio);
_serializers.insert(mtpc_documentAttributeFilename, _serialize_documentAttributeFilename);
_serializers.insert(mtpc_documentAttributeHasStickers, _serialize_documentAttributeHasStickers);
_serializers.insert(mtpc_messages_stickersNotModified, _serialize_messages_stickersNotModified);
_serializers.insert(mtpc_messages_stickers, _serialize_messages_stickers);
_serializers.insert(mtpc_stickerPack, _serialize_stickerPack);
@ -8854,6 +9044,7 @@ namespace {
_serializers.insert(mtpc_keyboardButtonRequestPhone, _serialize_keyboardButtonRequestPhone);
_serializers.insert(mtpc_keyboardButtonRequestGeoLocation, _serialize_keyboardButtonRequestGeoLocation);
_serializers.insert(mtpc_keyboardButtonSwitchInline, _serialize_keyboardButtonSwitchInline);
_serializers.insert(mtpc_keyboardButtonGame, _serialize_keyboardButtonGame);
_serializers.insert(mtpc_keyboardButtonRow, _serialize_keyboardButtonRow);
_serializers.insert(mtpc_replyKeyboardHide, _serialize_replyKeyboardHide);
_serializers.insert(mtpc_replyKeyboardForceReply, _serialize_replyKeyboardForceReply);
@ -8953,6 +9144,10 @@ namespace {
_serializers.insert(mtpc_messages_stickerSetInstallResultSuccess, _serialize_messages_stickerSetInstallResultSuccess);
_serializers.insert(mtpc_messages_stickerSetInstallResultArchive, _serialize_messages_stickerSetInstallResultArchive);
_serializers.insert(mtpc_stickerSetCovered, _serialize_stickerSetCovered);
_serializers.insert(mtpc_stickerSetMultiCovered, _serialize_stickerSetMultiCovered);
_serializers.insert(mtpc_maskCoords, _serialize_maskCoords);
_serializers.insert(mtpc_inputStickeredMediaPhoto, _serialize_inputStickeredMediaPhoto);
_serializers.insert(mtpc_inputStickeredMediaDocument, _serialize_inputStickeredMediaDocument);
_serializers.insert(mtpc_req_pq, _serialize_req_pq);
_serializers.insert(mtpc_req_DH_params, _serialize_req_DH_params);
@ -8968,6 +9163,7 @@ namespace {
_serializers.insert(mtpc_auth_sendInvites, _serialize_auth_sendInvites);
_serializers.insert(mtpc_auth_bindTempAuthKey, _serialize_auth_bindTempAuthKey);
_serializers.insert(mtpc_auth_cancelCode, _serialize_auth_cancelCode);
_serializers.insert(mtpc_auth_dropTempAuthKeys, _serialize_auth_dropTempAuthKeys);
_serializers.insert(mtpc_account_registerDevice, _serialize_account_registerDevice);
_serializers.insert(mtpc_account_unregisterDevice, _serialize_account_unregisterDevice);
_serializers.insert(mtpc_account_updateNotifySettings, _serialize_account_updateNotifySettings);
@ -9002,6 +9198,7 @@ namespace {
_serializers.insert(mtpc_messages_readFeaturedStickers, _serialize_messages_readFeaturedStickers);
_serializers.insert(mtpc_messages_saveRecentSticker, _serialize_messages_saveRecentSticker);
_serializers.insert(mtpc_messages_clearRecentStickers, _serialize_messages_clearRecentStickers);
_serializers.insert(mtpc_messages_setInlineGameScore, _serialize_messages_setInlineGameScore);
_serializers.insert(mtpc_upload_saveFilePart, _serialize_upload_saveFilePart);
_serializers.insert(mtpc_upload_saveBigFilePart, _serialize_upload_saveBigFilePart);
_serializers.insert(mtpc_help_saveAppLog, _serialize_help_saveAppLog);
@ -9081,6 +9278,7 @@ namespace {
_serializers.insert(mtpc_messages_sendInlineBotResult, _serialize_messages_sendInlineBotResult);
_serializers.insert(mtpc_messages_editMessage, _serialize_messages_editMessage);
_serializers.insert(mtpc_messages_getAllDrafts, _serialize_messages_getAllDrafts);
_serializers.insert(mtpc_messages_setGameScore, _serialize_messages_setGameScore);
_serializers.insert(mtpc_channels_createChannel, _serialize_channels_createChannel);
_serializers.insert(mtpc_channels_editAdmin, _serialize_channels_editAdmin);
_serializers.insert(mtpc_channels_editTitle, _serialize_channels_editTitle);
@ -9107,8 +9305,8 @@ namespace {
_serializers.insert(mtpc_messages_sendEncryptedService, _serialize_messages_sendEncryptedService);
_serializers.insert(mtpc_messages_receivedQueue, _serialize_messages_receivedQueue);
_serializers.insert(mtpc_photos_deletePhotos, _serialize_photos_deletePhotos);
_serializers.insert(mtpc_messages_getStickers, _serialize_messages_getStickers);
_serializers.insert(mtpc_messages_getAllStickers, _serialize_messages_getAllStickers);
_serializers.insert(mtpc_messages_getMaskStickers, _serialize_messages_getMaskStickers);
_serializers.insert(mtpc_messages_getWebPagePreview, _serialize_messages_getWebPagePreview);
_serializers.insert(mtpc_messages_exportChatInvite, _serialize_messages_exportChatInvite);
_serializers.insert(mtpc_channels_exportInvite, _serialize_channels_exportInvite);
@ -9124,8 +9322,8 @@ namespace {
_serializers.insert(mtpc_messages_getPeerDialogs, _serialize_messages_getPeerDialogs);
_serializers.insert(mtpc_messages_getFeaturedStickers, _serialize_messages_getFeaturedStickers);
_serializers.insert(mtpc_messages_getRecentStickers, _serialize_messages_getRecentStickers);
_serializers.insert(mtpc_messages_getUnusedStickers, _serialize_messages_getUnusedStickers);
_serializers.insert(mtpc_messages_getArchivedStickers, _serialize_messages_getArchivedStickers);
_serializers.insert(mtpc_messages_getAttachedStickers, _serialize_messages_getAttachedStickers);
_serializers.insert(mtpc_updates_getState, _serialize_updates_getState);
_serializers.insert(mtpc_updates_getDifference, _serialize_updates_getDifference);
_serializers.insert(mtpc_updates_getChannelDifference, _serialize_updates_getChannelDifference);

File diff suppressed because it is too large Load diff

View file

@ -90,8 +90,9 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
thumb = readStorageImageLocation(stream);
MTPDdocumentAttributeSticker::Flags stickerFlags = 0;
if (typeOfSet == StickerSetTypeEmpty) {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
attributes.push_back(MTP_documentAttributeSticker(MTP_flags(stickerFlags), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords()));
} else if (info) {
if (info->setId == Stickers::DefaultSetId || info->setId == Stickers::CloudRecentSetId || info->setId == Stickers::CustomSetId) {
typeOfSet = StickerSetTypeEmpty;
@ -99,14 +100,14 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
switch (typeOfSet) {
case StickerSetTypeID: {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetID(MTP_long(info->setId), MTP_long(info->accessHash))));
attributes.push_back(MTP_documentAttributeSticker(MTP_flags(stickerFlags), MTP_string(alt), MTP_inputStickerSetID(MTP_long(info->setId), MTP_long(info->accessHash)), MTPMaskCoords()));
} break;
case StickerSetTypeShortName: {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetShortName(MTP_string(info->shortName))));
attributes.push_back(MTP_documentAttributeSticker(MTP_flags(stickerFlags), MTP_string(alt), MTP_inputStickerSetShortName(MTP_string(info->shortName)), MTPMaskCoords()));
} break;
case StickerSetTypeEmpty:
default: {
attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty()));
attributes.push_back(MTP_documentAttributeSticker(MTP_flags(stickerFlags), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords()));
} break;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,664 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/twidget.h"
#include "ui/boxshadow.h"
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
namespace internal {
constexpr int InlineItemsMaxPerRow = 5;
constexpr int EmojiColorsCount = 5;
using InlineResult = InlineBots::Result;
using InlineResults = QList<InlineBots::Result*>;
using InlineItem = InlineBots::Layout::ItemBase;
struct InlineCacheEntry {
~InlineCacheEntry() {
clearResults();
}
QString nextOffset;
QString switchPmText, switchPmStartToken;
InlineResults results; // owns this results list
void clearResults();
};
class EmojiColorPicker : public TWidget {
Q_OBJECT
public:
EmojiColorPicker();
void showEmoji(uint32 code);
void paintEvent(QPaintEvent *e);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void step_appearance(float64 ms, bool timer);
void step_selected(uint64 ms, bool timer);
void showStart();
void clearSelection(bool fast = false);
public slots:
void hideStart(bool fast = false);
signals:
void emojiSelected(EmojiPtr emoji);
void hidden();
private:
void drawVariant(Painter &p, int variant);
void updateSelected();
bool _ignoreShow = false;
EmojiPtr _variants[EmojiColorsCount + 1];
typedef QMap<int32, uint64> EmojiAnimations; // index - showing, -index - hiding
EmojiAnimations _emojiAnimations;
Animation _a_selected;
float64 _hovers[EmojiColorsCount + 1];
int _selected = -1;
int _pressedSel = -1;
QPoint _lastMousePos;
bool _hiding = false;
QPixmap _cache;
anim::fvalue a_opacity;
Animation _a_appearance;
QTimer _hideTimer;
BoxShadow _shadow;
};
class EmojiPanel;
class EmojiPanInner : public ScrolledWidget {
Q_OBJECT
public:
EmojiPanInner();
void setMaxHeight(int32 h);
void paintEvent(QPaintEvent *e) override;
void step_selected(uint64 ms, bool timer);
void hideFinish();
void showEmojiPack(DBIEmojiTab packIndex);
void clearSelection(bool fast = false);
DBIEmojiTab currentTab(int yOffset) const;
void refreshRecent();
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void fillPanels(QVector<EmojiPanel*> &panels);
void refreshPanels(QVector<EmojiPanel*> &panels);
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEvent(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void enterFromChildEvent(QEvent *e, QWidget *child) override;
public slots:
void updateSelected();
void onShowPicker();
void onPickerHidden();
void onColorSelected(EmojiPtr emoji);
bool checkPickerHide();
signals:
void selected(EmojiPtr emoji);
void switchToStickers();
void scrollToY(int y);
void disableScroll(bool dis);
void needRefreshPanels();
void saveConfigDelayed(int32 delay);
private:
int32 _maxHeight;
int countHeight();
void selectEmoji(EmojiPtr emoji);
QRect emojiRect(int tab, int sel);
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _animations;
Animation _a_selected;
int _visibleTop = 0;
int _visibleBottom = 0;
int _counts[emojiTabCount];
QVector<EmojiPtr> _emojis[emojiTabCount];
QVector<float64> _hovers[emojiTabCount];
int32 _esize;
int _selected = -1;
int _pressedSel = -1;
int _pickerSel = -1;
QPoint _lastMousePos;
EmojiColorPicker _picker;
QTimer _showPickerTimer;
};
struct StickerIcon {
StickerIcon(uint64 setId) : setId(setId) {
}
StickerIcon(uint64 setId, DocumentData *sticker, int32 pixw, int32 pixh) : setId(setId), sticker(sticker), pixw(pixw), pixh(pixh) {
}
uint64 setId;
DocumentData *sticker = nullptr;
int pixw = 0;
int pixh = 0;
};
class StickerPanInner : public ScrolledWidget {
Q_OBJECT
public:
StickerPanInner();
void setMaxHeight(int32 h);
void paintEvent(QPaintEvent *e) override;
void step_selected(uint64 ms, bool timer);
void hideFinish(bool completely);
void showFinish();
void showStickerSet(uint64 setId);
void updateShowingSavedGifs();
bool showSectionIcons() const;
void clearSelection(bool fast = false);
void refreshStickers();
void refreshRecentStickers(bool resize = true);
void refreshSavedGifs();
int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted);
void refreshRecent();
void inlineBotChanged();
void hideInlineRowsPanel();
void clearInlineRowsPanel();
void fillIcons(QList<StickerIcon> &icons);
void fillPanels(QVector<EmojiPanel*> &panels);
void refreshPanels(QVector<EmojiPanel*> &panels);
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void preloadImages();
uint64 currentSet(int yOffset) const;
void notify_inlineItemLayoutChanged(const InlineItem *layout);
void ui_repaintInlineItem(const InlineItem *layout);
bool ui_isInlineItemVisible(const InlineItem *layout);
bool ui_isInlineItemBeingChosen();
bool inlineResultsShown() const {
return (_section == Section::Inlines);
}
int countHeight(bool plain = false);
~StickerPanInner();
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEvent(QEvent *e) override;
void leaveToChildEvent(QEvent *e, QWidget *child) override;
void enterFromChildEvent(QEvent *e, QWidget *child) override;
private slots:
void updateSelected();
void onSettings();
void onPreview();
void onUpdateInlineItems();
void onSwitchPm();
void onImageLoaded();
signals:
void selected(DocumentData *sticker);
void selected(PhotoData *photo);
void selected(InlineBots::Result *result, UserData *bot);
void displaySet(quint64 setId);
void installSet(quint64 setId);
void removeSet(quint64 setId);
void refreshIcons();
void emptyInlineRows();
void switchToEmoji();
void scrollToY(int y);
void scrollUpdated();
void disableScroll(bool dis);
void needRefreshPanels();
void saveConfigDelayed(int32 delay);
private:
struct Set {
Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), hovers(hoversSize, 0), pack(pack) {
}
uint64 id;
MTPDstickerSet::Flags flags;
QString title;
QVector<float64> hovers;
StickerPack pack;
};
using Sets = QList<Set>;
Sets &shownSets() {
return (_section == Section::Featured) ? _featuredSets : _mySets;
}
const Sets &shownSets() const {
return const_cast<StickerPanInner*>(this)->shownSets();
}
int featuredRowHeight() const;
void readVisibleSets();
bool showingInlineItems() const { // Gifs or Inline results
return (_section == Section::Inlines) || (_section == Section::Gifs);
}
void paintInlineItems(Painter &p, const QRect &r);
void paintStickers(Painter &p, const QRect &r);
void paintSticker(Painter &p, Set &set, int y, int index);
bool featuredHasAddButton(int index) const;
int featuredContentWidth() const;
QRect featuredAddRect(int y) const;
void refreshSwitchPmButton(const InlineCacheEntry *entry);
enum class AppendSkip {
Archived,
Installed,
};
void appendSet(Sets &to, uint64 setId, AppendSkip skip);
void selectEmoji(EmojiPtr emoji);
QRect stickerRect(int tab, int sel);
int32 _maxHeight;
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _animations;
Animation _a_selected;
int _visibleTop = 0;
int _visibleBottom = 0;
Sets _mySets;
Sets _featuredSets;
QList<bool> _custom;
enum class Section {
Inlines,
Gifs,
Featured,
Stickers,
};
Section _section = Section::Stickers;
bool _setGifCommand = false;
UserData *_inlineBot;
QString _inlineBotTitle;
uint64 _lastScrolled = 0;
QTimer _updateInlineItems;
bool _inlineWithThumb = false;
std_::unique_ptr<BoxButton> _switchPmButton;
QString _switchPmStartToken;
typedef QVector<InlineItem*> InlineItems;
struct InlineRow {
InlineRow() : height(0) {
}
int32 height;
InlineItems items;
};
typedef QVector<InlineRow> InlineRows;
InlineRows _inlineRows;
void clearInlineRows(bool resultsDeleted);
using GifLayouts = QMap<DocumentData*, InlineItem*>;
GifLayouts _gifLayouts;
InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position);
using InlineLayouts = QMap<InlineResult*, InlineItem*>;
InlineLayouts _inlineLayouts;
InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position);
bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth);
bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false);
InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0);
void deleteUnusedGifLayouts();
void deleteUnusedInlineLayouts();
int validateExistingInlineRows(const InlineResults &results);
void selectInlineResult(int row, int column);
void removeRecentSticker(int tab, int index);
int _selected = -1;
int _pressed = -1;
int _selectedFeaturedSet = -1;
int _pressedFeaturedSet = -1;
int _selectedFeaturedSetAdd = -1;
int _pressedFeaturedSetAdd = -1;
QPoint _lastMousePos;
QString _addText;
int _addWidth;
LinkButton _settings;
QTimer _previewTimer;
bool _previewShown = false;
};
class EmojiPanel : public TWidget {
Q_OBJECT
public:
EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool special, int32 wantedY); // Stickers::NoneSetId if in emoji
void setText(const QString &text);
void setDeleteVisible(bool isVisible);
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
int32 wantedY() const {
return _wantedY;
}
void setWantedY(int32 y) {
_wantedY = y;
}
signals:
void deleteClicked(quint64 setId);
void mousePressed();
public slots:
void onDelete();
private:
void updateText();
int32 _wantedY;
QString _text, _fullText;
uint64 _setId;
bool _special, _deleteVisible;
IconedButton *_delete;
};
class EmojiSwitchButton : public Button {
public:
EmojiSwitchButton(QWidget *parent, bool toStickers); // otherwise toEmoji
void paintEvent(QPaintEvent *e);
void updateText(const QString &inlineBotUsername = QString());
protected:
bool _toStickers;
QString _text;
int32 _textWidth;
};
} // namespace internal
class EmojiPan : public TWidget, public RPCSender {
Q_OBJECT
public:
EmojiPan(QWidget *parent);
void setMaxHeight(int32 h);
void paintEvent(QPaintEvent *e);
void moveBottom(int32 bottom, bool force = false);
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void otherEnter();
void otherLeave();
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
bool event(QEvent *e);
void fastHide();
bool hiding() const {
return _hiding || _hideTimer.isActive();
}
void step_appearance(float64 ms, bool timer);
void step_slide(float64 ms, bool timer);
void step_icons(uint64 ms, bool timer);
bool eventFilter(QObject *obj, QEvent *e);
void stickersInstalled(uint64 setId);
void queryInlineBot(UserData *bot, PeerData *peer, QString query);
void clearInlineBot();
bool overlaps(const QRect &globalRect) {
if (isHidden() || !_cache.isNull()) return false;
return QRect(st::dropdownDef.padding.left(),
st::dropdownDef.padding.top(),
_width - st::dropdownDef.padding.left() - st::dropdownDef.padding.right(),
_height - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom()
).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
void notify_inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout);
void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemBeingChosen();
bool inlineResultsShown() const {
return s_inner.inlineResultsShown();
}
public slots:
void refreshStickers();
void refreshSavedGifs();
void hideStart();
void hideFinish();
void showStart();
void onWndActiveChanged();
void onTabChange();
void onScrollEmoji();
void onScrollStickers();
void onSwitch();
void onDisplaySet(quint64 setId);
void onInstallSet(quint64 setId);
void onRemoveSet(quint64 setId);
void onRemoveSetSure();
void onDelayedHide();
void onRefreshIcons();
void onRefreshPanels();
void onSaveConfig();
void onSaveConfigDelayed(int32 delay);
void onInlineRequest();
void onEmptyInlineRows();
signals:
void emojiSelected(EmojiPtr emoji);
void stickerSelected(DocumentData *sticker);
void photoSelected(PhotoData *photo);
void inlineResultSelected(InlineBots::Result *result, UserData *bot);
void updateStickers();
private:
bool preventAutoHide() const;
void installSetDone(const MTPmessages_StickerSetInstallResult &result);
bool installSetFail(uint64 setId, const RPCError &error);
void paintStickerSettingsIcon(Painter &p) const;
void paintFeaturedStickerSetsBadge(Painter &p, int iconLeft) const;
void validateSelectedIcon(bool animated = false);
void updateContentHeight();
void leaveToChildEvent(QEvent *e, QWidget *child);
void hideAnimated();
void prepareShowHideCache();
void updateSelected();
void updateIcons();
void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab);
void updatePanelsPositions(const QVector<internal::EmojiPanel*> &panels, int32 st);
void showAll();
void hideAll();
int32 _maxHeight, _contentMaxHeight, _contentHeight, _contentHeightEmoji, _contentHeightStickers;
bool _horizontal = false;
bool _noTabUpdate = false;
int32 _width, _height, _bottom;
bool _hiding = false;
QPixmap _cache;
anim::fvalue a_opacity = { 0. };
Animation _a_appearance;
QTimer _hideTimer;
BoxShadow _shadow;
FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols;
QList<internal::StickerIcon> _icons;
QVector<float64> _iconHovers;
int _iconOver = -1;
int _iconSel = 0;
int _iconDown = -1;
bool _iconsDragging = false;
typedef QMap<int32, uint64> Animations; // index - showing, -index - hiding
Animations _iconAnimations;
Animation _a_icons;
QPoint _iconsMousePos, _iconsMouseDown;
int _iconsLeft = 0;
int _iconsTop = 0;
int _iconsStartX = 0;
int _iconsMax = 0;
anim::ivalue _iconsX = { 0, 0 };
anim::ivalue _iconSelX = { 0, 0 };
uint64 _iconsStartAnim = 0;
bool _stickersShown = false;
bool _shownFromInlineQuery = false;
QPixmap _fromCache, _toCache;
anim::ivalue a_fromCoord, a_toCoord;
anim::fvalue a_fromAlpha, a_toAlpha;
Animation _a_slide;
ScrollArea e_scroll;
internal::EmojiPanInner e_inner;
QVector<internal::EmojiPanel*> e_panels;
internal::EmojiSwitchButton e_switch;
ScrollArea s_scroll;
internal::StickerPanInner s_inner;
QVector<internal::EmojiPanel*> s_panels;
internal::EmojiSwitchButton s_switch;
uint64 _displayingSetId = 0;
uint64 _removingSetId = 0;
QTimer _saveConfigTimer;
// inline bots
typedef QMap<QString, internal::InlineCacheEntry*> InlineCache;
InlineCache _inlineCache;
QTimer _inlineRequestTimer;
void inlineBotChanged();
int32 showInlineRows(bool newResults);
bool hideOnNoInlineResults();
void recountContentMaxHeight();
bool refreshInlineRows(int32 *added = 0);
UserData *_inlineBot = nullptr;
PeerData *_inlineQueryPeer = nullptr;
QString _inlineQuery, _inlineNextQuery, _inlineNextOffset;
mtpRequestId _inlineRequestId = 0;
void inlineResultsDone(const MTPmessages_BotResults &result);
bool inlineResultsFail(const RPCError &error);
};

View file

@ -0,0 +1,207 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "stickers.h"
#include "boxes/stickersetbox.h"
#include "boxes/confirmbox.h"
#include "lang.h"
#include "apiwrap.h"
#include "localstorage.h"
#include "mainwidget.h"
namespace Stickers {
namespace {
constexpr int kReadFeaturedSetsTimeoutMs = 1000;
internal::FeaturedReader *FeaturedReaderInstance = nullptr;
} // namespace
void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) {
auto &v = d.vsets.c_vector().v;
auto &order = Global::RefStickerSetsOrder();
Stickers::Order archived;
archived.reserve(v.size());
QMap<uint64, uint64> setsToRequest;
for_const (auto &stickerSet, v) {
const MTPDstickerSet *setData = nullptr;
switch (stickerSet.type()) {
case mtpc_stickerSetCovered: {
auto &d = stickerSet.c_stickerSetCovered();
if (d.vset.type() == mtpc_stickerSet) {
setData = &d.vset.c_stickerSet();
}
} break;
case mtpc_stickerSetMultiCovered: {
auto &d = stickerSet.c_stickerSetMultiCovered();
if (d.vset.type() == mtpc_stickerSet) {
setData = &d.vset.c_stickerSet();
}
} break;
}
if (setData) {
auto set = Stickers::feedSet(*setData);
if (set->stickers.isEmpty()) {
setsToRequest.insert(set->id, set->access);
}
auto index = order.indexOf(set->id);
if (index >= 0) {
order.removeAt(index);
}
archived.push_back(set->id);
}
}
if (!setsToRequest.isEmpty()) {
for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) {
App::api()->scheduleStickerSetRequest(i.key(), i.value());
}
App::api()->requestStickerSets();
}
Local::writeInstalledStickers();
Local::writeArchivedStickers();
Ui::showLayer(new StickersBox(archived), KeepOtherLayers);
emit App::main()->stickersUpdated();
}
void installLocally(uint64 setId) {
auto &sets = Global::RefStickerSets();
auto it = sets.find(setId);
if (it == sets.end()) {
return;
}
auto flags = it->flags;
it->flags &= ~(MTPDstickerSet::Flag::f_archived | MTPDstickerSet_ClientFlag::f_unread);
it->flags |= MTPDstickerSet::Flag::f_installed;
auto changedFlags = flags ^ it->flags;
auto &order = Global::RefStickerSetsOrder();
int insertAtIndex = 0, currentIndex = order.indexOf(setId);
if (currentIndex != insertAtIndex) {
if (currentIndex > 0) {
order.removeAt(currentIndex);
}
order.insert(insertAtIndex, setId);
}
auto custom = sets.find(Stickers::CustomSetId);
if (custom != sets.cend()) {
for_const (auto sticker, it->stickers) {
int removeIndex = custom->stickers.indexOf(sticker);
if (removeIndex >= 0) custom->stickers.removeAt(removeIndex);
}
if (custom->stickers.isEmpty()) {
sets.erase(custom);
}
}
Local::writeInstalledStickers();
if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) Local::writeFeaturedStickers();
if (changedFlags & MTPDstickerSet::Flag::f_archived) {
auto index = Global::RefArchivedStickerSetsOrder().indexOf(setId);
if (index >= 0) {
Global::RefArchivedStickerSetsOrder().removeAt(index);
Local::writeArchivedStickers();
}
}
emit App::main()->stickersUpdated();
}
void undoInstallLocally(uint64 setId) {
auto &sets = Global::RefStickerSets();
auto it = sets.find(setId);
if (it == sets.end()) {
return;
}
it->flags &= ~MTPDstickerSet::Flag::f_installed;
auto &order = Global::RefStickerSetsOrder();
int currentIndex = order.indexOf(setId);
if (currentIndex >= 0) {
order.removeAt(currentIndex);
}
Local::writeInstalledStickers();
emit App::main()->stickersUpdated();
Ui::showLayer(new InformBox(lang(lng_stickers_not_found)), KeepOtherLayers);
}
void markFeaturedAsRead(uint64 setId) {
if (!FeaturedReaderInstance) {
if (auto main = App::main()) {
FeaturedReaderInstance = new internal::FeaturedReader(main);
} else {
return;
}
}
FeaturedReaderInstance->scheduleRead(setId);
}
namespace internal {
void readFeaturedDone() {
Local::writeFeaturedStickers();
if (App::main()) {
emit App::main()->stickersUpdated();
}
}
FeaturedReader::FeaturedReader(QObject *parent) : QObject(parent)
, _timer(new QTimer(this)) {
_timer->setSingleShot(true);
connect(_timer, SIGNAL(timeout()), this, SLOT(onReadSets()));
}
void FeaturedReader::scheduleRead(uint64 setId) {
if (!_setIds.contains(setId)) {
_setIds.insert(setId);
_timer->start(kReadFeaturedSetsTimeoutMs);
}
}
void FeaturedReader::onReadSets() {
auto &sets = Global::RefStickerSets();
auto count = Global::FeaturedStickerSetsUnreadCount();
QVector<MTPlong> wrappedIds;
wrappedIds.reserve(_setIds.size());
for_const (auto setId, _setIds) {
auto it = sets.find(setId);
if (it != sets.cend()) {
it->flags &= ~MTPDstickerSet_ClientFlag::f_unread;
wrappedIds.append(MTP_long(setId));
if (count) {
--count;
}
}
}
_setIds.clear();
if (!wrappedIds.empty()) {
MTP::send(MTPmessages_ReadFeaturedStickers(MTP_vector<MTPlong>(wrappedIds)), rpcDone(&readFeaturedDone));
Global::SetFeaturedStickerSetsUnreadCount(count);
}
}
} // namespace internal
} // namespace Stickers

View file

@ -0,0 +1,49 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Stickers {
void applyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d);
void installLocally(uint64 setId);
void undoInstallLocally(uint64 setId);
void markFeaturedAsRead(uint64 setId);
namespace internal {
class FeaturedReader : public QObject {
Q_OBJECT
public:
FeaturedReader(QObject *parent);
void scheduleRead(uint64 setId);
private slots:
void onReadSets();
private:
QTimer *_timer;
OrderedSet<uint64> _setIds;
};
} // namespace internal
} // namespace Stickers

View file

@ -0,0 +1,60 @@
/*
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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
using "basic.style";
featuredStickersHeader: 45px;
featuredStickersSkip: 15px;
featuredStickersHeaderFont: semiboldFont;
featuredStickersHeaderFg: windowTextFg;
featuredStickersHeaderTop: 0px;
featuredStickersSubheaderFont: normalFont;
featuredStickersSubheaderFg: #777;
featuredStickersSubheaderTop: 20px;
featuredStickersAddTop: 3px;
featuredStickersAdd: RoundButton(defaultActiveButton) {
width: -17px;
height: 26px;
textTop: 4px;
downTextTop: 5px;
}
stickerEmojiSkip: 5px;
stickersAddIcon: icon {
{ "stickers_add", #ffffff },
};
stickersAddSize: size(30px, 24px);
stickersFeaturedHeight: 32px;
stickersFeaturedFont: contactsNameFont;
stickersFeaturedPosition: point(16px, 6px);
stickersFeaturedBadgeFont: semiboldFont;
stickersFeaturedBadgeSize: 21px;
stickersFeaturedPen: contactsNewItemFg;
stickersFeaturedUnreadBg: msgFileInBg;
stickersFeaturedUnreadSize: 5px;
stickersFeaturedUnreadSkip: 5px;
stickersFeaturedUnreadTop: 7px;
stickersFeaturedInstalled: icon {
{ "mediaview_save_check", #40ace3 }
};

View file

@ -1140,7 +1140,7 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
for (int32 i = 0, l = attributes.size(); i < l; ++i) {
switch (attributes[i].type()) {
case mtpc_documentAttributeImageSize: {
const auto &d(attributes[i].c_documentAttributeImageSize());
auto &d = attributes[i].c_documentAttributeImageSize();
dimensions = QSize(d.vw.v, d.vh.v);
} break;
case mtpc_documentAttributeAnimated: if (type == FileDocument || type == StickerDocument || type == VideoDocument) {
@ -1148,15 +1148,17 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
_additional = nullptr;
} break;
case mtpc_documentAttributeSticker: {
const auto &d(attributes[i].c_documentAttributeSticker());
auto &d = attributes[i].c_documentAttributeSticker();
if (type == FileDocument) {
type = StickerDocument;
_additional = std_::make_unique<StickerData>();
}
if (sticker()) {
sticker()->alt = qs(d.valt);
if (sticker()->set.type() != mtpc_inputStickerSetID || d.vstickerset.type() == mtpc_inputStickerSetID) {
sticker()->set = d.vstickerset;
}
}
} break;
case mtpc_documentAttributeVideo: {
const auto &d(attributes[i].c_documentAttributeVideo());

View file

@ -1029,12 +1029,10 @@ struct DocumentAdditionalData {
};
struct StickerData : public DocumentAdditionalData {
StickerData() : set(MTP_inputStickerSetEmpty()) {
}
ImagePtr img;
QString alt;
MTPInputStickerSet set;
MTPInputStickerSet set = MTP_inputStickerSetEmpty();
bool setInstalled() const;
StorageImageLocation loc; // doc thumb location

View file

@ -438,7 +438,7 @@ CountrySelectBox::CountrySelectBox() : ItemListBox(st::countriesScroll, st::boxW
connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
connect(&_filter, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
connect(&_filterCancel, SIGNAL(clicked()), this, SLOT(onFilterCancel()));
connect(&_inner, SIGNAL(mustScrollTo(int, int)), &_scroll, SLOT(scrollToY(int, int)));
connect(&_inner, SIGNAL(mustScrollTo(int, int)), scrollArea(), SLOT(scrollToY(int, int)));
connect(&_inner, SIGNAL(countryChosen(const QString&)), this, SIGNAL(countryChosen(const QString&)));
_filterCancel.setAttribute(Qt::WA_OpaquePaintEvent);
@ -456,9 +456,9 @@ void CountrySelectBox::keyPressEvent(QKeyEvent *e) {
} else if (e->key() == Qt::Key_Up) {
_inner.selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown) {
_inner.selectSkipPage(_scroll.height(), 1);
_inner.selectSkipPage(scrollArea()->height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
_inner.selectSkipPage(_scroll.height(), -1);
_inner.selectSkipPage(scrollArea()->height(), -1);
} else {
ItemListBox::keyPressEvent(e);
}
@ -496,7 +496,7 @@ void CountrySelectBox::onFilterCancel() {
}
void CountrySelectBox::onFilterUpdate() {
_scroll.scrollToY(0);
scrollArea()->scrollToY(0);
if (_filter.getLastText().isEmpty()) {
_filterCancel.hide();
} else {

View file

@ -39,6 +39,7 @@
'<(src_loc)/overview/overview.style',
'<(src_loc)/profile/profile.style',
'<(src_loc)/settings/settings.style',
'<(src_loc)/stickers/stickers.style',
'<(src_loc)/ui/widgets/widgets.style',
],
'langpacks': [
@ -185,6 +186,8 @@
'<(src_loc)/boxes/report_box.h',
'<(src_loc)/boxes/sessionsbox.cpp',
'<(src_loc)/boxes/sessionsbox.h',
'<(src_loc)/boxes/sharebox.cpp',
'<(src_loc)/boxes/sharebox.h',
'<(src_loc)/boxes/stickersetbox.cpp',
'<(src_loc)/boxes/stickersetbox.h',
'<(src_loc)/boxes/usernamebox.cpp',
@ -390,6 +393,10 @@
'<(src_loc)/settings/settings_scale_widget.h',
'<(src_loc)/settings/settings_widget.cpp',
'<(src_loc)/settings/settings_widget.h',
'<(src_loc)/stickers/emoji_pan.cpp',
'<(src_loc)/stickers/emoji_pan.h',
'<(src_loc)/stickers/stickers.cpp',
'<(src_loc)/stickers/stickers.h',
'<(src_loc)/ui/buttons/history_down_button.cpp',
'<(src_loc)/ui/buttons/history_down_button.h',
'<(src_loc)/ui/buttons/icon_button.cpp',