diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 46b95ce1c..6ae7881c1 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -430,7 +430,7 @@ void ApiWrap::requestBots(ChannelData *peer) { void ApiWrap::gotChat(PeerData *peer, const MTPmessages_Chats &result) { _peerRequests.remove(peer); - + if (result.type() == mtpc_messages_chats) { const QVector &v(result.c_messages_chats().vchats.c_vector().v); bool badVersion = false; @@ -682,10 +682,11 @@ void ApiWrap::requestStickerSets() { void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) { _stickerSetRequests.remove(setId); - + if (result.type() != mtpc_messages_stickerSet) return; const MTPDmessages_stickerSet &d(result.c_messages_stickerSet()); - + const QVector &v(d.vpacks.c_vector().v); + if (d.vset.type() != mtpc_stickerSet) return; const MTPDstickerSet &s(d.vset.c_stickerSet()); @@ -731,12 +732,15 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) ++i; } } + + Global::StickersByEmoji_RemovePack(it->stickers); if (pack.isEmpty()) { int32 removeIndex = cStickerSetsOrder().indexOf(setId); if (removeIndex >= 0) cRefStickerSetsOrder().removeAt(removeIndex); sets.erase(it); } else { it->stickers = pack; + Global::StickersByEmoji_AddPack(it->stickers); } if (writeRecent) { diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 5898cf1ac..31194084e 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1969,6 +1969,7 @@ namespace App { if (api()) api()->clearWebPageRequests(); cSetRecentStickers(RecentStickerPack()); cSetStickerSets(StickerSets()); + Global::SetStickersByEmoji(StickersByEmojiMap()); cSetStickerSetsOrder(StickerSetsOrder()); cSetLastStickersUpdate(0); cSetSavedGifs(SavedGifs()); diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index b47ee602f..b7f62f22b 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -50,7 +50,7 @@ void StickerSetInner::gotSet(const MTPmessages_StickerSet &set) { for (int32 i = 0, l = v.size(); i < l; ++i) { DocumentData *doc = App::feedDocument(v.at(i)); if (!doc || !doc->sticker()) continue; - + _pack.push_back(doc); } if (d.vset.type() == mtpc_stickerSet) { @@ -92,6 +92,7 @@ void StickerSetInner::installDone(const MTPBool &result) { _setFlags &= ~MTPDstickerSet::flag_disabled; sets.insert(_setId, StickerSet(_setId, _setAccess, _setTitle, _setShortName, _setCount, _setHash, _setFlags)).value().stickers = _pack; + Global::StickersByEmoji_AddPack(_pack); StickerSetsOrder &order(cRefStickerSetsOrder()); int32 insertAtIndex = 0, currentIndex = order.indexOf(_setId); @@ -507,7 +508,7 @@ void StickersInner::onUpdateSelected() { float64 StickersInner::aboveShadowOpacity() const { if (_above < 0) return 0; - + int32 dx = 0; int32 dy = qAbs(_above * _rowHeight + _rows.at(_above)->yadd.current() - _started * _rowHeight); return qMin((dx + dy) * 2. / _rowHeight, 1.); @@ -613,7 +614,7 @@ void StickersInner::rebuild() { clear(); const StickerSetsOrder &order(cStickerSetsOrder()); _animStartTimes.reserve(order.size()); - + const StickerSets &sets(cStickerSets()); for (int32 i = 0, l = order.size(); i < l; ++i) { StickerSets::const_iterator it = sets.constFind(order.at(i)); @@ -867,6 +868,7 @@ void StickersBox::onSave() { if (removeIndex >= 0) cRefStickerSetsOrder().removeAt(removeIndex); sets.erase(it); } + Global::StickersByEmoji_RemovePack(it->stickers); } } } @@ -879,6 +881,7 @@ void StickersBox::onSave() { MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName)); _disenableRequests.insert(MTP::send(MTPmessages_InstallStickerSet(setId, MTP_boolFalse()), rpcDone(&StickersBox::disenableDone), rpcFail(&StickersBox::disenableFail), 0, 5), NullType()); it->flags &= ~MTPDstickerSet::flag_disabled; + Global::StickersByEmoji_AddPack(it->stickers); } order.push_back(reorder.at(i)); } @@ -887,6 +890,7 @@ void StickersBox::onSave() { if (it->id == CustomStickerSetId || it->id == RecentStickerSetId || order.contains(it->id)) { ++it; } else { + Global::StickersByEmoji_RemovePack(it->stickers); it = sets.erase(it); } } diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index faa204bfb..df94fa22f 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -3564,6 +3564,7 @@ void EmojiPan::onRemoveSetSure() { Ui::hideLayer(); StickerSets::iterator it = cRefStickerSets().find(_removingSetId); if (it != cRefStickerSets().cend() && !(it->flags & MTPDstickerSet::flag_official)) { + Global::StickersByEmoji_RemovePack(it->stickers); if (it->id && it->access) { MTP::send(MTPmessages_UninstallStickerSet(MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)))); } else if (!it->shortName.isEmpty()) { @@ -3841,24 +3842,74 @@ void EmojiPan::recountContentMaxHeight() { updateContentHeight(); } -MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows) +MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerByEmojiRows *srows) : _parent(parent) , _mrows(mrows) , _hrows(hrows) , _brows(brows) +, _srows(srows) +, _stickersPerRow(1) +, _recentInlineBotsInRows(0) , _sel(-1) , _mouseSel(false) , _overDelete(false) { } void MentionsInner::paintEvent(QPaintEvent *e) { - QPainter p(this); + Painter p(this); + + QRect r(e->rect()); + if (r != rect()) p.setClipRect(r); int32 atwidth = st::mentionFont->width('@'), hashwidth = st::mentionFont->width('#'); int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize; int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right(); int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::lineWidth, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; + if (!_srows->isEmpty()) { + int32 rows = rowscount(_srows->size(), _stickersPerRow); + int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows); + int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows); + int32 fromcol = floorclamp(r.x() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow); + int32 tocol = ceilclamp(r.x() + r.width() - st::stickerPanPadding, st::stickerPanSize.width(), 0, _stickersPerRow); + for (int32 row = fromrow; row < torow; ++row) { + for (int32 col = fromcol; col < tocol; ++col) { + int32 index = row * _stickersPerRow + col; + if (index >= _srows->size()) break; + + DocumentData *sticker = _srows->at(index); + if (!sticker->sticker()) continue; + + QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height()); + if (_sel == index) { + QPoint tl(pos); + if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width()); + App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); + } + + bool goodThumb = !sticker->thumb->isNull() && ((sticker->thumb->width() >= 128) || (sticker->thumb->height() >= 128)); + if (goodThumb) { + sticker->thumb->load(); + } else { + sticker->checkSticker(); + } + + float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height())); + if (coef > 1) coef = 1; + int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height()); + if (w < 1) w = 1; + if (h < 1) h = 1; + QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); + if (goodThumb) { + p.drawPixmapLeft(ppos, width(), sticker->thumb->pix(w, h)); + } else if (!sticker->sticker()->img->isNull()) { + p.drawPixmapLeft(ppos, width(), sticker->sticker()->img->pix(w, h)); + } + } + } + return; + } + int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1; int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); bool hasUsername = _parent->filter().indexOf('@') > 1; @@ -3970,6 +4021,10 @@ void MentionsInner::paintEvent(QPaintEvent *e) { p.fillRect(cWideMode() ? st::lineWidth : 0, _parent->innerBottom() - st::lineWidth, width() - (cWideMode() ? st::lineWidth : 0), st::lineWidth, st::shadowColor->b); } +void MentionsInner::resizeEvent(QResizeEvent *e) { + _stickersPerRow = int32(width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()); +} + void MentionsInner::mouseMoveEvent(QMouseEvent *e) { _mousePos = mapToGlobal(e->pos()); _mouseSel = true; @@ -3978,29 +4033,47 @@ void MentionsInner::mouseMoveEvent(QMouseEvent *e) { void MentionsInner::clearSel() { _mouseSel = _overDelete = false; - setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty()) ? -1 : 0); + setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty() && _srows->isEmpty()) ? -1 : 0); } -bool MentionsInner::moveSel(int direction) { +bool MentionsInner::moveSel(int key) { _mouseSel = false; - int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size()); + int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size()); + int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0); + if (!_srows->isEmpty()) { + if (key == Qt::Key_Left) { + direction = -1; + } else if (key == Qt::Key_Right) { + direction = 1; + } else { + direction *= _stickersPerRow; + } + } if (_sel >= maxSel || _sel < 0) { - if (direction < 0) { + if (direction < -1) { + setSel(((maxSel - 1) / _stickersPerRow) * _stickersPerRow, true); + } else if (direction < 0) { setSel(maxSel - 1, true); } else { setSel(0, true); } return (_sel >= 0 && _sel < maxSel); } - setSel((_sel + direction >= maxSel) ? -1 : (_sel + direction), true); + setSel((_sel + direction >= maxSel || _sel + direction < 0) ? -1 : (_sel + direction), true); return true; } bool MentionsInner::select() { - QString sel = getSelected(); - if (!sel.isEmpty()) { - emit chosen(sel); - return true; + if (!_srows->isEmpty()) { + if (_sel >= 0 && _sel < _srows->size()) { + emit selected(_srows->at(_sel)); + } + } else { + QString sel = getSelected(); + if (!sel.isEmpty()) { + emit chosen(sel); + return true; + } } return false; } @@ -4086,21 +4159,51 @@ void MentionsInner::leaveEvent(QEvent *e) { } } +void MentionsInner::updateSelectedRow() { + if (_sel >= 0) { + if (_srows->isEmpty()) { + update(0, _sel * st::mentionHeight, width(), st::mentionHeight); + } else { + int32 row = _sel / _stickersPerRow, col = _sel % _stickersPerRow; + update(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanSize.width(), st::stickerPanSize.height()); + } + } +} + void MentionsInner::setSel(int sel, bool scroll) { - if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight); + updateSelectedRow(); _sel = sel; - if (_sel >= 0) update(0, _sel * st::mentionHeight, width(), st::mentionHeight); - int32 maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); - if (scroll && _sel >= 0 && _sel < maxSel) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight); + updateSelectedRow(); + + if (scroll && _sel >= 0) { + if (_srows->isEmpty()) { + emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight); + } else { + int32 row = _sel / _stickersPerRow; + emit mustScrollTo(st::stickerPanPadding + row * st::stickerPanSize.height(), st::stickerPanPadding + (row + 1) * st::stickerPanSize.height()); + } + } } void MentionsInner::onUpdateSelected(bool force) { QPoint mouse(mapFromGlobal(_mousePos)); if ((!force && !rect().contains(mouse)) || !_mouseSel) return; - int w = width(), mouseY = mouse.y(); - int32 sel = mouseY / int32(st::mentionHeight), maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); - _overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= w - st::mentionHeight) : false; + int32 sel = -1, maxSel = 0; + if (!_srows->isEmpty()) { + int32 rows = rowscount(_srows->size(), _stickersPerRow); + int32 row = (mouse.y() >= st::stickerPanPadding) ? ((mouse.y() - st::stickerPanPadding) / st::stickerPanSize.height()) : -1; + int32 col = (mouse.x() >= st::stickerPanPadding) ? ((mouse.x() - st::stickerPanPadding) / st::stickerPanSize.width()) : -1; + if (row >= 0 && col >= 0) { + sel = row * _stickersPerRow + col; + } + maxSel = _srows->size(); + _overDelete = false; + } else { + sel = mouse.y() / int32(st::mentionHeight); + maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); + _overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false; + } if (sel < 0 || sel >= maxSel) { sel = -1; } @@ -4119,7 +4222,7 @@ void MentionsInner::onParentGeometryChanged() { MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent) , _scroll(this, st::mentionScroll) -, _inner(this, &_mrows, &_hrows, &_brows) +, _inner(this, &_mrows, &_hrows, &_brows, &_srows) , _chat(0) , _user(0) , _channel(0) @@ -4130,6 +4233,7 @@ MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent) _hideTimer.setSingleShot(true); connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart())); connect(&_inner, SIGNAL(chosen(QString)), this, SIGNAL(chosen(QString))); + connect(&_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*))); connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int))); connect(App::wnd(), SIGNAL(imageLoaded()), &_inner, SLOT(update())); @@ -4150,7 +4254,7 @@ MentionsDropdown::MentionsDropdown(QWidget *parent) : TWidget(parent) } void MentionsDropdown::paintEvent(QPaintEvent *e) { - QPainter p(this); + Painter p(this); if (_a_appearance.animating()) { p.setOpacity(a_opacity.current()); @@ -4158,29 +4262,43 @@ void MentionsDropdown::paintEvent(QPaintEvent *e) { return; } - p.fillRect(rect(), st::white->b); - + p.fillRect(rect(), st::white); } void MentionsDropdown::showFiltered(PeerData *peer, QString query, bool start) { - if (query.isEmpty() || (peer->isUser() && query.at(0) == '@' && (!start || cRecentInlineBots().isEmpty()))) { - if (!isHidden()) { - hideStart(); - } - return; - } - _chat = peer->asChat(); _user = peer->asUser(); _channel = peer->asChannel(); + if (query.isEmpty()) { + rowsUpdated(MentionRows(), HashtagRows(), BotCommandRows(), _srows, false); + return; + } + + _emoji = EmojiPtr(); + query = query.toLower(); - bool toDown = (_filter != query); - if (toDown) { + bool resetScroll = (_filter != query); + if (resetScroll) { _filter = query; } _addInlineBots = start; - updateFiltered(toDown); + updateFiltered(resetScroll); +} + +void MentionsDropdown::showStickers(EmojiPtr emoji) { + bool resetScroll = (_emoji != emoji); + _emoji = emoji; + if (!emoji) { + rowsUpdated(_mrows, _hrows, _brows, StickerByEmojiRows(), false); + return; + } + + _chat = 0; + _user = 0; + _channel = 0; + + updateFiltered(resetScroll); } bool MentionsDropdown::clearFilteredBotCommands() { @@ -4189,12 +4307,22 @@ bool MentionsDropdown::clearFilteredBotCommands() { return true; } -void MentionsDropdown::updateFiltered(bool toDown) { +void MentionsDropdown::updateFiltered(bool resetScroll) { int32 now = unixtime(), recentInlineBots = 0; MentionRows mrows; HashtagRows hrows; BotCommandRows brows; - if (_filter.at(0) == '@') { + StickerByEmojiRows srows; + if (_emoji) { + const StickersByEmojiMap &stickers(Global::StickersByEmoji()); + StickersByEmojiMap::const_iterator it = stickers.constFind(emojiGetNoColor(_emoji)); + if (it != stickers.cend() && !it->isEmpty()) { + srows.reserve(it->size()); + for (StickersByEmojiList::const_iterator i = it->cbegin(), e = it->cend(); i != e; ++i) { + srows.push_back(i.key()); + } + } + } else if (_filter.at(0) == '@') { if (_chat) { mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size())); } else if (_channel && _channel->isMegagroup()) { @@ -4214,46 +4342,46 @@ void MentionsDropdown::updateFiltered(bool toDown) { ++recentInlineBots; } } - } - if (_filter.at(0) == '@' && _chat) { - QMultiMap ordered; - mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size())); - if (_chat->noParticipantInfo()) { - if (App::api()) App::api()->requestFullPeer(_chat); - } else if (!_chat->participants.isEmpty()) { - for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { - UserData *user = i.key(); - if (user->username.isEmpty()) continue; - if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; - ordered.insertMulti(App::onlineForSort(user, now), user); + if (_chat) { + QMultiMap ordered; + mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size())); + if (_chat->noParticipantInfo()) { + if (App::api()) App::api()->requestFullPeer(_chat); + } else if (!_chat->participants.isEmpty()) { + for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) { + UserData *user = i.key(); + if (user->username.isEmpty()) continue; + if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; + ordered.insertMulti(App::onlineForSort(user, now), user); + } } - } - for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) { - UserData *user = *i; - if (user->username.isEmpty()) continue; - if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; - mrows.push_back(user); - if (!ordered.isEmpty()) { - ordered.remove(App::onlineForSort(user, now), user); - } - } - if (!ordered.isEmpty()) { - for (QMultiMap::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) { - --i; - mrows.push_back(i.value()); - } - } - } else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) { - QMultiMap ordered; - if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) { - if (App::api()) App::api()->requestLastParticipants(_channel); - } else { - mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size()); - for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) { + for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) { UserData *user = *i; if (user->username.isEmpty()) continue; if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; mrows.push_back(user); + if (!ordered.isEmpty()) { + ordered.remove(App::onlineForSort(user, now), user); + } + } + if (!ordered.isEmpty()) { + for (QMultiMap::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) { + --i; + mrows.push_back(i.value()); + } + } + } else if (_channel && _channel->isMegagroup()) { + QMultiMap ordered; + if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) { + if (App::api()) App::api()->requestLastParticipants(_channel); + } else { + mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size()); + for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) { + UserData *user = *i; + if (user->username.isEmpty()) continue; + if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue; + mrows.push_back(user); + } } } } else if (_filter.at(0) == '#') { @@ -4332,28 +4460,31 @@ void MentionsDropdown::updateFiltered(bool toDown) { } } } - rowsUpdated(mrows, hrows, brows, toDown); + rowsUpdated(mrows, hrows, brows, srows, resetScroll); _inner.setRecentInlineBotsInRows(recentInlineBots); } -void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown) { - if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty()) { +void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerByEmojiRows &srows, bool resetScroll) { + if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.isEmpty()) { if (!isHidden()) { hideStart(); } _mrows.clear(); _hrows.clear(); _brows.clear(); + _srows.clear(); } else { _mrows = mrows; _hrows = hrows; _brows = brows; + _srows = srows; + bool hidden = _hiding || isHidden(); if (hidden) { show(); _scroll.show(); } - recount(toDown); + recount(resetScroll); if (hidden) { hide(); showStart(); @@ -4363,31 +4494,37 @@ void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows & void MentionsDropdown::setBoundings(QRect boundings) { _boundings = boundings; - resize(_boundings.width(), height()); - _scroll.resize(size()); - _inner.resize(width(), _inner.height()); recount(); } -void MentionsDropdown::recount(bool toDown) { - int32 h = (_mrows.isEmpty() ? (_hrows.isEmpty() ? _brows.size() : _hrows.size()) : _mrows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst; +void MentionsDropdown::recount(bool resetScroll) { + int32 h = 0, oldst = _scroll.scrollTop(), st = oldst, maxh = 4.5 * st::mentionHeight; + if (!_srows.isEmpty()) { + int32 stickersPerRow = int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width()); + int32 rows = rowscount(_srows.size(), stickersPerRow); + h = st::stickerPanPadding + rows * st::stickerPanSize.height(); + } else if (!_mrows.isEmpty()) { + h = _mrows.size() * st::mentionHeight; + } else if (!_hrows.isEmpty()) { + h = _hrows.size() * st::mentionHeight; + } else if (!_brows.isEmpty()) { + h = _brows.size() * st::mentionHeight; + } - if (_inner.height() != h) { -// st += h - _inner.height(); - _inner.resize(width(), h); + if (_inner.width() != _boundings.width() || _inner.height() != h) { + _inner.resize(_boundings.width(), h); } if (h > _boundings.height()) h = _boundings.height(); - if (h > 4.5 * st::mentionHeight) h = 4.5 * st::mentionHeight; - if (height() != h) { -// st += _scroll.height() - h; - setGeometry(0, _boundings.height() - h, width(), h); - _scroll.resize(width(), h); + if (h > maxh) h = maxh; + if (width() != _boundings.width() || height() != h) { + setGeometry(0, _boundings.height() - h, _boundings.width(), h); + _scroll.resize(_boundings.width(), h); } else if (y() != _boundings.height() - h) { move(0, _boundings.height() - h); } - if (toDown) st = 0;// _scroll.scrollTopMax(); + if (resetScroll) st = 0; if (st != oldst) _scroll.scrollToY(st); - if (toDown) _inner.clearSel(); + if (resetScroll) _inner.clearSel(); } void MentionsDropdown::fastHide() { @@ -4487,11 +4624,8 @@ bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) { if (isHidden()) return QWidget::eventFilter(obj, e); if (e->type() == QEvent::KeyPress) { QKeyEvent *ev = static_cast(e); - if (ev->key() == Qt::Key_Up) { - _inner.moveSel(-1); - return true; - } else if (ev->key() == Qt::Key_Down) { - return _inner.moveSel(1); + if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down || (!_srows.isEmpty() && (ev->key() == Qt::Key_Left || ev->key() == Qt::Key_Right))) { + return _inner.moveSel(ev->key()); } else if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) { return _inner.select(); } diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index b0cac7d40..e6dcd1d3d 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -727,6 +727,7 @@ private: typedef QList MentionRows; typedef QList HashtagRows; typedef QList > BotCommandRows; +typedef QList StickerByEmojiRows; class MentionsDropdown; class MentionsInner : public TWidget { @@ -734,9 +735,10 @@ class MentionsInner : public TWidget { public: - MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows); + MentionsInner(MentionsDropdown *parent, MentionRows *mrows, HashtagRows *hrows, BotCommandRows *brows, StickerByEmojiRows *srows); void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *e); void enterEvent(QEvent *e); void leaveEvent(QEvent *e); @@ -745,7 +747,7 @@ public: void mouseMoveEvent(QMouseEvent *e); void clearSel(); - bool moveSel(int direction); + bool moveSel(int key); bool select(); void setRecentInlineBotsInRows(int32 bots); @@ -755,6 +757,7 @@ public: signals: void chosen(QString mentionOrHashtag); + void selected(DocumentData *sticker); void mustScrollTo(int scrollToTop, int scrollToBottom); public slots: @@ -764,13 +767,15 @@ public slots: private: + void updateSelectedRow(); void setSel(int sel, bool scroll = false); MentionsDropdown *_parent; MentionRows *_mrows; HashtagRows *_hrows; BotCommandRows *_brows; - int32 _recentInlineBotsInRows; + StickerByEmojiRows *_srows; + int32 _stickersPerRow, _recentInlineBotsInRows; int32 _sel; bool _mouseSel; QPoint _mousePos; @@ -791,7 +796,8 @@ public: bool clearFilteredBotCommands(); void showFiltered(PeerData *peer, QString query, bool start); - void updateFiltered(bool toDown = false); + void showStickers(EmojiPtr emoji); + void updateFiltered(bool resetScroll = false); void setBoundings(QRect boundings); void step_appearance(float64 ms, bool timer); @@ -807,6 +813,10 @@ public: bool eventFilter(QObject *obj, QEvent *e); QString getSelected() const; + bool stickersShown() const { + return !_srows.isEmpty(); + } + bool overlaps(const QRect &globalRect) { if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false; @@ -818,6 +828,7 @@ public: signals: void chosen(QString mentionOrHashtag); + void stickerSelected(DocumentData *sticker); public slots: @@ -828,14 +839,15 @@ public slots: private: - void recount(bool toDown = false); + void recount(bool resetScroll = false); QPixmap _cache; MentionRows _mrows; HashtagRows _hrows; BotCommandRows _brows; + StickerByEmojiRows _srows; - void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown); + void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, const StickerByEmojiRows &srows, bool resetScroll); ScrollArea _scroll; MentionsInner _inner; @@ -843,6 +855,7 @@ private: ChatData *_chat; UserData *_user; ChannelData *_channel; + EmojiPtr _emoji; QString _filter; QRect _boundings; bool _addInlineBots; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 09b132740..bdfd52eea 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -174,3 +174,83 @@ namespace Notify { } } + +namespace Global { + + struct Data { + uint64 LaunchId; + + StickersByEmojiMap StickersByEmoji; + }; + + Data *_data = 0; + + Initializer::Initializer() { + initThirdParty(); + _data = new Data(); + memset_rand(&_data->LaunchId, sizeof(_data->LaunchId)); + } + + Initializer::~Initializer() { + deinitThirdParty(); + } + +#define DefineGlobalReadOnly(Type, Name) const Type &Name() { \ + t_assert_full(_data != 0, "_data is null in Global::" #Name, __FILE__, __LINE__); \ + return _data->Name; \ +} +#define DefineGlobal(Type, Name) DefineGlobalReadOnly(Type, Name) \ +void Set##Name(const Type &Name) { \ + t_assert_full(_data != 0, "_data is null in Global::Set" #Name, __FILE__, __LINE__); \ + _data->Name = Name; \ +} \ +Type &Ref##Name() { \ + t_assert_full(_data != 0, "_data is null in Global::Ref" #Name, __FILE__, __LINE__); \ + return _data->Name; \ +} + + DefineGlobalReadOnly(uint64, LaunchId); + + DefineGlobal(StickersByEmojiMap, StickersByEmoji); + + void StickersByEmoji_Add(DocumentData *doc) { + if (StickerData *sticker = doc->sticker()) { + if (EmojiPtr emoji = emojiGetNoColor(emojiFromText(sticker->alt))) { + RefStickersByEmoji()[emoji].insert(doc); + } + } + } + + bool StickersByEmoji_Remove(DocumentData *doc) { + if (StickerData *sticker = doc->sticker()) { + if (EmojiPtr emoji = emojiGetNoColor(emojiFromText(sticker->alt))) { + StickersByEmojiMap stickers(RefStickersByEmoji()); + StickersByEmojiMap::iterator iList = stickers.find(emoji); + if (iList != stickers.cend()) { + StickersByEmojiList::iterator iEntry = iList->find(doc); + if (iEntry != iList->cend()) { + iList->erase(iEntry); + if (iList->isEmpty()) { + stickers.erase(iList); + } + return true; + } + } + } + } + return false; + } + + void StickersByEmoji_AddPack(const StickerPack &pack) { + for (StickerPack::const_iterator i = pack.cbegin(), e = pack.cend(); i != e; ++i) { + StickersByEmoji_Add(*i); + } + } + + void StickersByEmoji_RemovePack(const StickerPack &pack) { + for (StickerPack::const_iterator i = pack.cbegin(), e = pack.cend(); i != e; ++i) { + StickersByEmoji_Remove(*i); + } + } + +}; diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index ed7ee718c..14db6d2d1 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -95,3 +95,29 @@ namespace Notify { void automaticLoadSettingsChangedGif(); }; + +typedef OrderedSet StickersByEmojiList; +typedef QMap StickersByEmojiMap; + +namespace Global { + + class Initializer { + public: + Initializer(); + ~Initializer(); + }; + +#define DeclareGlobalReadOnly(Type, Name) const Type &Name(); +#define DeclareGlobal(Type, Name) DeclareGlobalReadOnly(Type, Name) \ + void Set##Name(const Type &Name); \ + Type &Ref##Name(); + + DeclareGlobalReadOnly(uint64, LaunchId); + + DeclareGlobal(StickersByEmojiMap, StickersByEmoji); + void StickersByEmoji_Add(DocumentData *doc); + bool StickersByEmoji_Remove(DocumentData *doc); + void StickersByEmoji_AddPack(const StickerPack &pack); + void StickersByEmoji_RemovePack(const StickerPack &pack); + +}; diff --git a/Telegram/SourceFiles/gui/emoji_config.h b/Telegram/SourceFiles/gui/emoji_config.h index e377374ba..1734d3d82 100644 --- a/Telegram/SourceFiles/gui/emoji_config.h +++ b/Telegram/SourceFiles/gui/emoji_config.h @@ -83,7 +83,7 @@ inline EmojiPtr emojiFromUrl(const QString &url) { return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e. } -inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) { +inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int *plen = 0) { EmojiPtr emoji = 0; if (ch + 1 < e && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) { uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode(); @@ -108,15 +108,15 @@ inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) { } else if (ch + 2 < e && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) { uint32 code = (ch->unicode() << 16) | (ch + 2)->unicode(); emoji = emojiGet(code); - len = emoji->len + 1; + if (plen) *plen = emoji->len + 1; return emoji; } else if (ch < e) { emoji = emojiGet(ch->unicode()); - Q_ASSERT(emoji != TwoSymbolEmoji); + t_assert(emoji != TwoSymbolEmoji); } if (emoji) { - len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0); + int32 len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0); if (emoji->color && (ch + len + 1 < e && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode())); EmojiPtr col = emojiGet(emoji, color); @@ -128,8 +128,21 @@ inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int &len) { } } } + if (plen) *plen = len; + } + + return emoji; +} + +inline EmojiPtr emojiFromText(const QString &text, int32 *plen = 0) { + return text.isEmpty() ? EmojiPtr(0) : emojiFromText(text.constBegin(), text.constEnd(), plen); +} + +inline EmojiPtr emojiGetNoColor(EmojiPtr emoji) { + if (emoji && emoji->color && (emoji->color & 0xFFFF0000U) != 0xFFFF0000U) { + EmojiPtr result = emojiGet(emoji->code); + return (result == TwoSymbolEmoji) ? emojiGet(emoji->code, emoji->code2) : result; } - return emoji; } @@ -180,7 +193,7 @@ inline QString replaceEmojis(const QString &text, EntitiesInText &entities) { if (canFindEmoji) { emojiFind(ch, e, newEmojiEnd, emojiCode); } - + while (currentEntity < entitiesCount && ch >= emojiStart + entities[currentEntity].offset + entities[currentEntity].length) { ++currentEntity; } diff --git a/Telegram/SourceFiles/gui/flatinput.cpp b/Telegram/SourceFiles/gui/flatinput.cpp index 5d0a25ace..a7f4241a0 100644 --- a/Telegram/SourceFiles/gui/flatinput.cpp +++ b/Telegram/SourceFiles/gui/flatinput.cpp @@ -66,7 +66,7 @@ FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString , _notingBene(0) , _st(st) { resize(_st.width, _st.height); - + setFont(_st.font->f); setAlignment(_st.align); @@ -959,7 +959,7 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) { const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch, ++fp) { int32 emojiLen = 0; - emoji = emojiFromText(ch, e, emojiLen); + emoji = emojiFromText(ch, e, &emojiLen); if (emoji) { if (replacePosition >= 0) { emoji = 0; // replace tilde char format first @@ -1331,7 +1331,7 @@ InputField::InputField(QWidget *parent, const style::InputField &st, const QStri connect(&_inner, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool))); connect(&_inner, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); if (App::wnd()) connect(&_inner, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); - + setCursor(style::cur_text); if (!val.isEmpty()) { _inner.setPlainText(val); @@ -1412,7 +1412,7 @@ void InputField::paintEvent(QPaintEvent *e) { if (_st.iconSprite.pxWidth()) { p.drawSpriteLeft(_st.iconPosition, width(), _st.iconSprite); } - + bool drawPlaceholder = _placeholderVisible; if (_a_placeholderShift.animating()) { p.setOpacity(a_placeholderOpacity.current()); @@ -1425,11 +1425,11 @@ void InputField::paintEvent(QPaintEvent *e) { QRect r(rect().marginsRemoved(_st.textMargins + _st.placeholderMargins)); r.moveLeft(r.left() + a_placeholderLeft.current()); if (rtl()) r.moveLeft(width() - r.left() - r.width()); - + p.setFont(_st.font); p.setPen(a_placeholderFg.current()); p.drawText(r, _placeholder, _st.placeholderAlign); - + p.restore(); } TWidget::paintEvent(e); @@ -1663,7 +1663,7 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { } int32 emojiLen = 0; - emoji = emojiFromText(ch, e, emojiLen); + emoji = emojiFromText(ch, e, &emojiLen); if (emoji) { if (replacePosition >= 0) { emoji = 0; // replace tilde char format first @@ -2028,7 +2028,7 @@ MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st, setStyle(&_inputFieldStyle); QLineEdit::setTextMargins(0, 0, 0, 0); setContentsMargins(0, 0, 0, 0); - + setAttribute(Qt::WA_AcceptTouchEvents); _touchTimer.setSingleShot(true); connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); diff --git a/Telegram/SourceFiles/gui/flattextarea.cpp b/Telegram/SourceFiles/gui/flattextarea.cpp index 4f27183e7..3340bb1ab 100644 --- a/Telegram/SourceFiles/gui/flattextarea.cpp +++ b/Telegram/SourceFiles/gui/flattextarea.cpp @@ -738,7 +738,7 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch, ++fp) { int32 emojiLen = 0; - emoji = emojiFromText(ch, e, emojiLen); + emoji = emojiFromText(ch, e, &emojiLen); if (emoji) { if (replacePosition >= 0) { emoji = 0; // replace tilde char format first diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index 328762ab2..0dad2f9f4 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -598,7 +598,7 @@ public: void parseEmojiFromCurrent() { int len = 0; - EmojiPtr e = emojiFromText(ptr - emojiLookback, end, len); + EmojiPtr e = emojiFromText(ptr - emojiLookback, end, &len); if (!e) return; for (int l = len - emojiLookback - 1; l > 0; --l) { @@ -4502,8 +4502,7 @@ goodCanBreakEntity = canBreakEntity;\ #undef MARK_GOOD_AS_LEVEL int elen = 0; - EmojiPtr e = emojiFromText(ch, end, elen); - if (e) { + if (EmojiPtr e = emojiFromText(ch, end, &elen)) { for (int i = 0; i < elen; ++i, ++ch, ++s) { if (ch->isHighSurrogate() && i + 1 < elen && (ch + 1)->isLowSurrogate()) { ++ch; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 7fc28f5f1..f76055744 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -4744,14 +4744,11 @@ bool HistoryGif::dataLoaded() const { HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia() , _pixw(1) , _pixh(1) -, _data(document) { +, _data(document) +, _emoji(_data->sticker()->alt) { _data->thumb->load(); - if (!_data->sticker()->alt.isEmpty()) { - _emoji = _data->sticker()->alt; - int32 elen = 0; - if (EmojiPtr e = emojiFromText(_emoji.constData(), _emoji.constEnd(), elen)) { - _emoji = emojiString(e); - } + if (EmojiPtr e = emojiFromText(_emoji)) { + _emoji = emojiString(e); } } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index bc412b9d0..f44979901 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2763,6 +2763,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _attachMention.hide(); connect(&_attachMention, SIGNAL(chosen(QString)), this, SLOT(onMentionHashtagOrBotCommandInsert(QString))); + connect(&_attachMention, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*))); _field.installEventFilter(&_attachMention); _field.setCtrlEnterSubmit(cCtrlEnter()); @@ -2807,7 +2808,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) } void HistoryWidget::start() { - connect(App::main(), SIGNAL(stickersUpdated()), &_emojiPan, SLOT(refreshStickers())); + connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated())); connect(App::main(), SIGNAL(savedGifsUpdated()), &_emojiPan, SLOT(refreshSavedGifs())); updateRecentStickers(); @@ -2816,6 +2817,11 @@ void HistoryWidget::start() { connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*))); } +void HistoryWidget::onStickersUpdated() { + _emojiPan.refreshStickers(); + updateStickersByEmoji(); +} + void HistoryWidget::onMentionHashtagOrBotCommandInsert(QString str) { if (str.at(0) == '/') { // bot command App::sendBotCommand(str); @@ -2867,8 +2873,23 @@ void HistoryWidget::updateInlineBotQuery() { } } +void HistoryWidget::updateStickersByEmoji() { + int32 len = 0; + if (EmojiPtr emoji = emojiFromText(_field.getLastText(), &len)) { + if (_field.getLastText().size() <= len) { + _attachMention.showStickers(emoji); + } else { + len = 0; + } + } + if (!len) { + _attachMention.showStickers(EmojiPtr(0)); + } +} + void HistoryWidget::onTextChange() { updateInlineBotQuery(); + updateStickersByEmoji(); if (_peer && (!_peer->isChannel() || _peer->isMegagroup() || !_peer->asChannel()->canPublish() || (!_peer->asChannel()->isBroadcast() && !_broadcast.checked()))) { if (!_inlineBot && (_textUpdateEventsFlags & TextUpdateEventsSendTyping)) { @@ -3180,6 +3201,7 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) { ++i; } } + Global::StickersByEmoji_RemovePack(it->stickers); it = sets.erase(it); } } @@ -5405,10 +5427,10 @@ void HistoryWidget::onFieldFocused() { } void HistoryWidget::onCheckMentionDropdown() { - if (!_history || _a_show.animating() || _inlineBot) return; + if (!_history || _a_show.animating()) return; bool start = false; - QString query = _field.getMentionHashtagBotCommandPart(start); + QString query = _inlineBot ? QString() : _field.getMentionHashtagBotCommandPart(start); if (!query.isEmpty()) { if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots(); if (query.at(0) == '@' && cRecentInlineBots().isEmpty()) Local::readRecentHashtagsAndBots(); @@ -6529,6 +6551,13 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti App::historyRegRandom(randomId, newId); + if (_attachMention.stickersShown()) { + setFieldText(QString()); + _saveDraftText = true; + _saveDraftStart = getms(); + onDraftSave(); + } + if (!_attachMention.isHidden()) _attachMention.hideStart(); if (!_attachType.isHidden()) _attachType.hideStart(); if (!_emojiPan.isHidden()) _emojiPan.hideStart(); @@ -6813,7 +6842,9 @@ void HistoryWidget::updatePreview() { void HistoryWidget::onCancel() { if (_inlineBot && _field.getLastText().startsWith('@' + _inlineBot->username + ' ')) { setFieldText(QString(), TextUpdateEventsSaveDraft, false); - } else { + } else if (!_attachMention.isHidden()) { + _attachMention.hideStart(); + } else { Ui::showChatsList(); emit cancelled(); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 1936ccbb4..6aae8e548 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -452,6 +452,7 @@ public: void updateFieldPlaceholder(); void updateInlineBotQuery(); + void updateStickersByEmoji(); void uploadImage(const QImage &img, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, const QString &source = QString(), bool withText = false); void uploadFile(const QString &file, PrepareMediaType type, FileLoadForceConfirmType confirm = FileLoadNoForceConfirm, bool withText = false); // with confirmation @@ -643,6 +644,7 @@ public slots: void onCmdStart(); void activate(); + void onStickersUpdated(); void onMentionHashtagOrBotCommandInsert(QString str); void onTextChange(); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 2e34afc7f..c527f8781 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -2920,6 +2920,8 @@ namespace Local { RecentStickerPack &recent(cRefRecentStickers()); recent.clear(); + Global::SetStickersByEmoji(StickersByEmojiMap()); + StickerSet &def(sets.insert(DefaultStickerSetId, StickerSet(DefaultStickerSetId, 0, lang(lng_stickers_default_set), QString(), 0, 0, MTPDstickerSet::flag_official)).value()); StickerSet &custom(sets.insert(CustomStickerSetId, StickerSet(CustomStickerSetId, 0, lang(lng_custom_stickers), QString(), 0, 0, 0)).value()); @@ -2993,6 +2995,8 @@ namespace Local { StickerSetsOrder &order(cRefStickerSetsOrder()); order.clear(); + Global::SetStickersByEmoji(StickersByEmojiMap()); + quint32 cnt; QByteArray hash; stickers.stream >> cnt >> hash; // ignore hash, it is counted @@ -3073,6 +3077,10 @@ namespace Local { set.stickers.push_back(doc); ++set.count; } + + if (setId != CustomStickerSetId) { + Global::StickersByEmoji_AddPack(set.stickers); + } } } diff --git a/Telegram/SourceFiles/logs.h b/Telegram/SourceFiles/logs.h index d1bd7199e..bb54a12d5 100644 --- a/Telegram/SourceFiles/logs.h +++ b/Telegram/SourceFiles/logs.h @@ -84,11 +84,13 @@ void logWrite(const QString &v); static volatile int *t_assert_nullptr = 0; inline void t_noop() {} -inline void t_assert_fail(const char *condition, const char *file, int32 line) { - LOG(("Assertion Failed! \"%1\" %2:%3").arg(condition).arg(file).arg(line)); +inline void t_assert_fail(const char *message, const char *file, int32 line) { + LOG(("Assertion Failed! %1 %2:%3").arg(message).arg(file).arg(line)); *t_assert_nullptr = 0; } -#define t_assert(cond) ((!(cond)) ? t_assert_fail(#cond, __FILE__, __LINE__) : t_noop()) +#define t_assert_full(condition, message, file, line) ((!(condition)) ? t_assert_fail(message, file, line) : t_noop()) +#define t_assert_c(condition, comment) t_assert_full(condition, "\"" #condition "\" (" comment ")", __FILE__, __LINE__) +#define t_assert(condition) t_assert_full(condition, "\"" #condition "\"", __FILE__, __LINE__) void logsInit(); void logsInitDebug(); diff --git a/Telegram/SourceFiles/main.cpp b/Telegram/SourceFiles/main.cpp index deb1e3918..a64c8641d 100644 --- a/Telegram/SourceFiles/main.cpp +++ b/Telegram/SourceFiles/main.cpp @@ -32,8 +32,6 @@ int main(int argc, char *argv[]) { //signal(SIGSEGV, _sigsegvHandler); #endif - LibrariesInitializer _init; - settingsParseArgs(argc, argv); for (int32 i = 0; i < argc; ++i) { if (string("-fixprevious") == argv[i]) { @@ -44,6 +42,8 @@ int main(int argc, char *argv[]) { } logsInit(); + Global::Initializer _init; + Local::readSettings(); if (Local::oldSettingsVersion() < AppVersion) { psNewVersion(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 5ba5539b8..c55324669 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -4607,8 +4607,14 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { const MTPDstickerSet &s(set.vset.c_stickerSet()); StickerSets &sets(cRefStickerSets()); - - sets.insert(s.vid.v, StickerSet(s.vid.v, s.vaccess_hash.v, stickerSetTitle(s), qs(s.vshort_name), s.vcount.v, s.vhash.v, s.vflags.v)).value().stickers = pack; + StickerSets::iterator it = sets.find(s.vid.v); + if (it != sets.cend()) { + Global::StickersByEmoji_RemovePack(it->stickers); + } else { + it = sets.insert(s.vid.v, StickerSet(s.vid.v, s.vaccess_hash.v, stickerSetTitle(s), qs(s.vshort_name), s.vcount.v, s.vhash.v, s.vflags.v)); + } + it->stickers = pack; + Global::StickersByEmoji_AddPack(pack); StickerSetsOrder &order(cRefStickerSetsOrder()); int32 insertAtIndex = 0, currentIndex = order.indexOf(s.vid.v); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 8b2700c4e..b5d400977 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -190,7 +190,6 @@ void settingsParseArgs(int argc, char *argv[]) { gCustomNotifies = false; } #endif - memset_rand(&gInstance, sizeof(gInstance)); gExeDir = psCurrentExeDirectory(argc, argv); gExeName = psCurrentExeName(argc, argv); for (int32 i = 0; i < argc; ++i) { diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 34bd47fba..de8acf32e 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1513,6 +1513,8 @@ void DocumentData::setattributes(const QVector &attributes _additional = 0; } break; case mtpc_documentAttributeSticker: { + bool wasByEmoji = Global::StickersByEmoji_Remove(this); + const MTPDdocumentAttributeSticker &d(attributes[i].c_documentAttributeSticker()); if (type == FileDocument) { type = StickerDocument; @@ -1522,6 +1524,7 @@ void DocumentData::setattributes(const QVector &attributes if (sticker()) { sticker()->alt = qs(d.valt); sticker()->set = d.vstickerset; + if (wasByEmoji) Global::StickersByEmoji_Add(this); } } break; case mtpc_documentAttributeVideo: { diff --git a/Telegram/SourceFiles/types.cpp b/Telegram/SourceFiles/types.cpp index 6d9e7e275..0b3406426 100644 --- a/Telegram/SourceFiles/types.cpp +++ b/Telegram/SourceFiles/types.cpp @@ -264,7 +264,7 @@ namespace { _MsStarter _msStarter; } -LibrariesInitializer::LibrariesInitializer() { +void initThirdParty() { if (!RAND_status()) { // should be always inited in all modern OS char buf[16]; memcpy(buf, &_msStart, 8); @@ -296,7 +296,7 @@ LibrariesInitializer::LibrariesInitializer() { _sslInited = true; } -LibrariesInitializer::~LibrariesInitializer() { +void deinitThirdParty() { av_lockmgr_register(0); delete[] _sslLocks; diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 0a6359661..264e4cfc1 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -133,11 +133,8 @@ inline void mylocaltime(struct tm * _Tm, const time_t * _Time) { #endif } -class LibrariesInitializer { -public: - LibrariesInitializer(); - ~LibrariesInitializer(); -}; +void initThirdParty(); // called by Global::Initializer +void deinitThirdParty(); bool checkms(); // returns true if time has changed uint64 getms(bool checked = false); @@ -458,6 +455,9 @@ MimeType mimeTypeForName(const QString &mime); MimeType mimeTypeForFile(const QFileInfo &file); MimeType mimeTypeForData(const QByteArray &data); +inline int32 rowscount(int32 count, int32 perrow) { + return (count + perrow - 1) / perrow; +} inline int32 floorclamp(int32 value, int32 step, int32 lowest, int32 highest) { return qMin(qMax(value / step, lowest), highest); }