From 7f6cf32cdddeb037a92d8cdd2c514455c57caf40 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 29 Mar 2016 20:17:00 +0300 Subject: [PATCH] ITextLink moved to ClickHandler, TextLinkPtr > ClickHandlerPtr. Global methods textlnkOver/Down/DrawOver were replaced by static members of ClickHandler, now global state consists of the handler pointer + host pointer, who declares callbacks for the active and pressed handler changed events. This will allow to use ClickHandler from different hosts simultaneously (like HistoryItem / BotDescription / BotKeyboard). Not yet tested. --- Telegram/Resources/lang.strings | 4 + Telegram/SourceFiles/app.cpp | 4 +- Telegram/SourceFiles/audio.cpp | 2 +- Telegram/SourceFiles/boxes/confirmbox.cpp | 46 +- Telegram/SourceFiles/boxes/confirmbox.h | 7 +- Telegram/SourceFiles/dropdown.cpp | 159 ++++--- Telegram/SourceFiles/dropdown.h | 1 - Telegram/SourceFiles/facades.cpp | 56 ++- Telegram/SourceFiles/facades.h | 8 +- Telegram/SourceFiles/gui/flatlabel.cpp | 43 +- Telegram/SourceFiles/gui/flatlabel.h | 9 +- Telegram/SourceFiles/gui/images.cpp | 12 +- Telegram/SourceFiles/gui/text.cpp | 241 ++++++----- Telegram/SourceFiles/gui/text.h | 363 ++++++++++------ Telegram/SourceFiles/history.cpp | 342 ++++++++------- Telegram/SourceFiles/history.h | 347 ++++++++++------ Telegram/SourceFiles/historywidget.cpp | 415 +++++++++---------- Telegram/SourceFiles/historywidget.h | 55 ++- Telegram/SourceFiles/intro/introphone.cpp | 13 +- Telegram/SourceFiles/layout.cpp | 197 ++++----- Telegram/SourceFiles/layout.h | 196 +++++---- Telegram/SourceFiles/mainwidget.cpp | 15 +- Telegram/SourceFiles/mainwidget.h | 4 +- Telegram/SourceFiles/mediaview.cpp | 61 ++- Telegram/SourceFiles/mediaview.h | 11 +- Telegram/SourceFiles/mtproto/scheme.tl | 5 + Telegram/SourceFiles/mtproto/scheme_auto.cpp | 63 +++ Telegram/SourceFiles/mtproto/scheme_auto.h | 224 ++++++++++ Telegram/SourceFiles/overviewwidget.cpp | 184 +++----- Telegram/SourceFiles/overviewwidget.h | 8 +- Telegram/SourceFiles/playerwidget.cpp | 2 +- Telegram/SourceFiles/profilewidget.cpp | 68 +-- Telegram/SourceFiles/profilewidget.h | 10 +- Telegram/SourceFiles/settingswidget.cpp | 14 +- Telegram/SourceFiles/settingswidget.h | 2 +- Telegram/SourceFiles/structs.cpp | 57 +-- Telegram/SourceFiles/structs.h | 149 +++---- Telegram/SourceFiles/types.h | 5 + Telegram/SourceFiles/window.cpp | 15 +- Telegram/SourceFiles/window.h | 5 +- 40 files changed, 1976 insertions(+), 1446 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index c5b317fdd..36b8f38c0 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -601,6 +601,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_forwarded_signed" = "{channel} ({user})"; "lng_in_reply_to" = "In reply to"; +"lng_bot_share_location_unavailable" = "Sorry, the location sharing is currently unavailable in Telegram Desktop."; +"lng_bot_share_phone" = "Share phone?"; +"lng_bot_share_phone_confirm" = "Share"; + "lng_attach_failed" = "Failed"; "lng_attach_file" = "File"; "lng_attach_photo" = "Photo"; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 1edee0a39..1713a564e 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -2040,8 +2040,8 @@ namespace App { } void clearHistories() { - textlnkOver(TextLinkPtr()); - textlnkDown(TextLinkPtr()); + ClickHandler::clearActive(); + ClickHandler::unpressed(); histories().clear(); diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp index 29205c7af..49a536ed1 100644 --- a/Telegram/SourceFiles/audio.cpp +++ b/Telegram/SourceFiles/audio.cpp @@ -506,7 +506,7 @@ void AudioPlayer::play(const SongMsgId &song, int64 position) { if (current->file.isEmpty() && current->data.isEmpty()) { setStoppedState(current); if (!song.song->loading()) { - DocumentOpenLink::doOpen(song.song); + DocumentOpenClickHandler::doOpen(song.song); } } else { current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 3e379aae0..53e02bb5e 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -83,33 +83,30 @@ void ConfirmBox::mouseMoveEvent(QMouseEvent *e) { void ConfirmBox::mousePressEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateHover(); - if (textlnkOver()) { - textlnkDown(textlnkOver()); - update(); - } + ClickHandler::pressed(); return LayeredWidget::mousePressEvent(e); } void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateHover(); - if (textlnkOver() && textlnkOver() == textlnkDown()) { + if (ClickHandlerPtr activated = ClickHandler::unpressed()) { Ui::hideLayer(); - textlnkOver()->onClick(e->button()); + App::activateClickHandler(activated, e->button()); } - textlnkDown(TextLinkPtr()); } void ConfirmBox::leaveEvent(QEvent *e) { - if (_myLink) { - if (textlnkOver() == _myLink) { - textlnkOver(TextLinkPtr()); - update(); - } - _myLink = TextLinkPtr(); - setCursor(style::cur_default); - update(); - } + ClickHandler::clearActive(this); +} + +void ConfirmBox::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + setCursor(active ? style::cur_pointer : style::cur_default); + update(); +} + +void ConfirmBox::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + update(); } void ConfirmBox::updateLink() { @@ -119,17 +116,12 @@ void ConfirmBox::updateLink() { void ConfirmBox::updateHover() { QPoint m(mapFromGlobal(_lastMousePos)); - bool wasMy = (_myLink == textlnkOver()); + textstyleSet(&st::boxTextStyle); - _myLink = _text.linkLeft(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, width(), (_text.maxWidth() < width()) ? style::al_center : style::al_left); + ClickHandlerPtr handler = _text.linkLeft(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, width(), (_text.maxWidth() < width()) ? style::al_center : style::al_left); textstyleRestore(); - if (_myLink != textlnkOver()) { - if (wasMy || _myLink || rect().contains(m)) { - textlnkOver(_myLink); - } - setCursor(_myLink ? style::cur_pointer : style::cur_default); - update(); - } + + ClickHandler::setActive(handler, this); } void ConfirmBox::closePressed() { @@ -182,9 +174,9 @@ ConfirmLinkBox::ConfirmLinkBox(const QString &url) : ConfirmBox(lang(lng_open_th void ConfirmLinkBox::onOpenLink() { Ui::hideLayer(); if (reMailStart().match(_url).hasMatch()) { - EmailLink(_url).onClick(Qt::LeftButton); + EmailClickHandler::doOpen(_url); } else { - TextLink(_url).onClick(Qt::LeftButton); + UrlClickHandler::doOpen(_url); } } diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index 05b8febf3..0931b238f 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "abstractbox.h" class InformBox; -class ConfirmBox : public AbstractBox { +class ConfirmBox : public AbstractBox, public ClickHandlerHost { Q_OBJECT public: @@ -38,6 +38,10 @@ public: void leaveEvent(QEvent *e); void updateLink(); + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active); + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed); + public slots: void onCancel(); @@ -69,7 +73,6 @@ private: void updateHover(); QPoint _lastMousePos; - TextLinkPtr _myLink; BoxButton _confirm, _cancel; }; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index ee475348b..23b138a26 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -1413,8 +1413,7 @@ void StickerPanInner::mousePressEvent(QMouseEvent *e) { updateSelected(); _pressedSel = _selected; - textlnkDown(textlnkOver()); - _linkDown = _linkOver; + ClickHandler::pressed(); _previewTimer.start(QApplication::startDragTime()); } @@ -1422,10 +1421,9 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { _previewTimer.stop(); int32 pressed = _pressedSel; - TextLinkPtr down(_linkDown); _pressedSel = -1; - _linkDown.reset(); - textlnkDown(TextLinkPtr()); + + ClickHandlerPtr activated = ClickHandler::unpressed(); _lastMousePos = e->globalPos(); updateSelected(); @@ -1435,71 +1433,71 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { return; } - if (_selected < 0 || _selected != pressed || _linkOver != down) return; + if (_selected < 0 || _selected != pressed || (_showingInlineItems && !activated)) return; if (_showingInlineItems) { - int32 row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; - if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) { - if (down) { - if (down->type() == qstr("SendInlineItemLink") && e->button() == Qt::LeftButton) { - LayoutInlineItem *item = _inlineRows.at(row).items.at(col); - PhotoData *photo = item->photo(); - DocumentData *doc = item->document(); - InlineResult *result = item->result(); - if (doc) { - if (doc->loaded()) { - emit selected(doc); - } else if (doc->loading()) { - doc->cancel(); - } else { - DocumentOpenLink::doOpen(doc, ActionOnLoadNone); - } - } else if (photo) { - if (photo->medium->loaded() || photo->thumb->loaded()) { - emit selected(photo); - } else if (!photo->medium->loading()) { - photo->thumb->loadEvenCancelled(); - photo->medium->loadEvenCancelled(); - } - } else if (result) { - if (result->type == qstr("gif")) { - if (result->doc) { - if (result->doc->loaded()) { - emit selected(result, _inlineBot); - } else if (result->doc->loading()) { - result->doc->cancel(); - } else { - DocumentOpenLink::doOpen(result->doc, ActionOnLoadNone); - } - } else if (result->loaded()) { - emit selected(result, _inlineBot); - } else if (result->loading()) { - result->cancelFile(); - Ui::repaintInlineItem(item); - } else { - result->saveFile(QString(), LoadFromCloudOrLocal, false); - Ui::repaintInlineItem(item); - } - } else if (result->type == qstr("photo")) { - if (result->photo) { - if (result->photo->medium->loaded() || result->photo->thumb->loaded()) { - emit selected(result, _inlineBot); - } else if (!result->photo->medium->loading()) { - result->photo->thumb->loadEvenCancelled(); - result->photo->medium->loadEvenCancelled(); - } - } else if (result->thumb->loaded()) { - emit selected(result, _inlineBot); - } else if (!result->thumb->loading()) { - result->thumb->loadEvenCancelled(); - Ui::repaintInlineItem(item); - } - } else { - emit selected(result, _inlineBot); - } + if (!dynamic_cast<SendInlineItemClickHandler*>(activated.data())) { + App::activateClickHandler(activated, e->button()); + return; + } + int row = _selected / MatrixRowShift, col = _selected % MatrixRowShift; + if (row >= _inlineRows.size() || col >= _inlineRows.at(row).items.size()) { + return; + } + + LayoutInlineItem *item = _inlineRows.at(row).items.at(col); + PhotoData *photo = item->photo(); + DocumentData *doc = item->document(); + InlineResult *result = item->result(); + if (doc) { + if (doc->loaded()) { + emit selected(doc); + } else if (doc->loading()) { + doc->cancel(); + } else { + DocumentOpenClickHandler::doOpen(doc, ActionOnLoadNone); + } + } else if (photo) { + if (photo->medium->loaded() || photo->thumb->loaded()) { + emit selected(photo); + } else if (!photo->medium->loading()) { + photo->thumb->loadEvenCancelled(); + photo->medium->loadEvenCancelled(); + } + } else if (result) { + if (result->type == qstr("gif")) { + if (result->doc) { + if (result->doc->loaded()) { + emit selected(result, _inlineBot); + } else if (result->doc->loading()) { + result->doc->cancel(); + } else { + DocumentOpenClickHandler::doOpen(result->doc, ActionOnLoadNone); } + } else if (result->loaded()) { + emit selected(result, _inlineBot); + } else if (result->loading()) { + result->cancelFile(); + Ui::repaintInlineItem(item); } else { - down->onClick(e->button()); + result->saveFile(QString(), LoadFromCloudOrLocal, false); + Ui::repaintInlineItem(item); } + } else if (result->type == qstr("photo")) { + if (result->photo) { + if (result->photo->medium->loaded() || result->photo->thumb->loaded()) { + emit selected(result, _inlineBot); + } else if (!result->photo->medium->loading()) { + result->photo->thumb->loadEvenCancelled(); + result->photo->medium->loadEvenCancelled(); + } + } else if (result->thumb->loaded()) { + emit selected(result, _inlineBot); + } else if (!result->thumb->loading()) { + result->thumb->loadEvenCancelled(); + Ui::repaintInlineItem(item); + } + } else { + emit selected(result, _inlineBot); } } return; @@ -1578,11 +1576,7 @@ void StickerPanInner::clearSelection(bool fast) { if (_selected >= 0) { int32 srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift; t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); - if (_linkOver) { - _inlineRows.at(srow).items.at(scol)->linkOut(_linkOver); - _linkOver = TextLinkPtr(); - textlnkOver(_linkOver); - } + ClickHandler::clearActive(_inlineRows.at(srow).items.at(scol)); setCursor(style::cur_default); } _selected = _pressedSel = -1; @@ -2197,7 +2191,8 @@ void StickerPanInner::updateSelected() { if (_showingInlineItems) { int sx = (rtl() ? width() - p.x() : p.x()) - st::inlineResultsLeft, sy = p.y() - st::emojiPanHeader; int32 row = -1, col = -1, sel = -1; - TextLinkPtr lnk; + ClickHandlerPtr lnk; + ClickHandlerHost *lnkhost = nullptr; HistoryCursorState cursor = HistoryDefaultCursorState; if (sy >= 0) { row = 0; @@ -2221,6 +2216,7 @@ void StickerPanInner::updateSelected() { if (col < inlineItems.size()) { sel = row * MatrixRowShift + col; inlineItems.at(col)->getState(lnk, cursor, sx, sy); + lnkhost = inlineItems.at(col); } else { row = col = -1; } @@ -2246,23 +2242,8 @@ void StickerPanInner::updateSelected() { } } } - if (_linkOver != lnk) { - if (_linkOver && srow >= 0 && scol >= 0) { - t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size()); - _inlineRows.at(srow).items.at(scol)->linkOut(_linkOver); - Ui::repaintInlineItem(_inlineRows.at(srow).items.at(scol)); - } - if ((_linkOver && !lnk) || (!_linkOver && lnk)) { - setCursor(lnk ? style::cur_pointer : style::cur_default); - } - _linkOver = lnk; - textlnkOver(lnk); - if (_linkOver && row >= 0 && col >= 0) { - t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size()); - _inlineRows.at(row).items.at(col)->linkOver(_linkOver); - Ui::repaintInlineItem(_inlineRows.at(row).items.at(col)); - } - + if (ClickHandler::setActive(lnk, lnkhost)) { + setCursor(lnk ? style::cur_pointer : style::cur_default); } return; } diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index f4d18ae30..45d0939d1 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -469,7 +469,6 @@ private: int32 validateExistingInlineRows(const InlineResults &results); int32 _selected, _pressedSel; QPoint _lastMousePos; - TextLinkPtr _linkOver, _linkDown; LinkButton _settings; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 9e955b272..3200ad4fa 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -24,16 +24,22 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "application.h" +#include "boxes/confirmbox.h" + #include "layerwidget.h" #include "lang.h" -Q_DECLARE_METATYPE(TextLinkPtr); +Q_DECLARE_METATYPE(ClickHandlerPtr); Q_DECLARE_METATYPE(Qt::MouseButton); namespace App { - void sendBotCommand(const QString &cmd, MsgId replyTo) { - if (MainWidget *m = main()) m->sendBotCommand(cmd, replyTo); + void sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo) { + if (MainWidget *m = main()) m->sendBotCommand(peer, cmd, replyTo); + } + + void sendBotCallback(PeerData *peer, const QString &cmd, MsgId replyTo) { + if (MainWidget *m = main()) m->sendBotCallback(peer, cmd, replyTo); } bool insertBotCommand(const QString &cmd, bool specialGif) { @@ -41,13 +47,36 @@ namespace App { return false; } - void activateBotCommand(const HistoryMessageReplyMarkup::Button &button, MsgId replyTo) { - QString cmd(button.text); - App::sendBotCommand(cmd, replyTo); + void activateBotCommand(PeerData *peer, const HistoryMessageReplyMarkup::Button &button, MsgId replyTo) { + switch (button.type) { + case HistoryMessageReplyMarkup::Button::Default: { + // copy string before passing it to the sending method + // the original button can be destroyed inside + sendBotCommand(peer, QString(button.text), replyTo); + } break; + + case HistoryMessageReplyMarkup::Button::Callback: { + sendBotCallback(peer, QString(button.text), replyTo); + } break; + + case HistoryMessageReplyMarkup::Button::Url: { + HiddenUrlClickHandler(button.url).onClick(Qt::LeftButton); + } break; + + case HistoryMessageReplyMarkup::Button::RequestLocation: { + Ui::showLayer(new InformBox(lang(lng_bot_share_location_unavailable))); + } break; + + case HistoryMessageReplyMarkup::Button::RequestPhone: { + ConfirmBox *box = new ConfirmBox(lang(lng_bot_share_phone), lang(lng_bot_share_phone_confirm)); + box->connect(box, SIGNAL(confirmed()), App::main(), SLOT(onShareBotLocation())); + Ui::showLayer(box); + } break; + } } void searchByHashtag(const QString &tag, PeerData *inPeer) { - if (MainWidget *m = main()) m->searchMessages(tag + ' ', (inPeer && inPeer->isChannel()) ? inPeer : 0); + if (MainWidget *m = main()) m->searchMessages(tag + ' ', (inPeer && inPeer->isChannel() && !inPeer->isMegagroup()) ? inPeer : 0); } void openPeerByName(const QString &username, MsgId msgId, const QString &startToken) { @@ -83,11 +112,11 @@ namespace App { } } - void activateTextLink(TextLinkPtr link, Qt::MouseButton button) { + void activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button) { if (Window *w = wnd()) { - qRegisterMetaType<TextLinkPtr>(); + qRegisterMetaType<ClickHandlerPtr>(); qRegisterMetaType<Qt::MouseButton>(); - QMetaObject::invokeMethod(w, "app_activateTextLink", Qt::QueuedConnection, Q_ARG(TextLinkPtr, link), Q_ARG(Qt::MouseButton, button)); + QMetaObject::invokeMethod(w, "app_activateClickHandler", Qt::QueuedConnection, Q_ARG(ClickHandlerPtr, handler), Q_ARG(Qt::MouseButton, button)); } } @@ -165,6 +194,13 @@ namespace Ui { } } + PeerData *getPeerForMouseAction() { + if (Window *w = App::wnd()) { + return w->ui_getPeerForMouseAction(); + } + return nullptr; + } + bool hideWindowNoQuit() { if (!App::quitting()) { if (Window *w = App::wnd()) { diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 462299987..207e3a693 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -24,9 +24,10 @@ class LayeredWidget; namespace App { - void sendBotCommand(const QString &cmd, MsgId replyTo = 0); + void sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo = 0); + void sendBotCallback(PeerData *peer, const QString &cmd, MsgId replyTo); bool insertBotCommand(const QString &cmd, bool specialGif = false); - void activateBotCommand(const HistoryMessageReplyMarkup::Button &button, MsgId replyTo = 0); + void activateBotCommand(PeerData *peer, const HistoryMessageReplyMarkup::Button &button, MsgId replyTo = 0); void searchByHashtag(const QString &tag, PeerData *inPeer); void openPeerByName(const QString &username, MsgId msgId = ShowAtUnreadMsgId, const QString &startToken = QString()); void joinGroupByHash(const QString &hash); @@ -36,7 +37,7 @@ namespace App { void removeDialog(History *history); void showSettings(); - void activateTextLink(TextLinkPtr link, Qt::MouseButton button); + void activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button); }; @@ -73,6 +74,7 @@ namespace Ui { inline void showChatsListAsync() { showPeerHistoryAsync(PeerId(0), 0); } + PeerData *getPeerForMouseAction(); bool hideWindowNoQuit(); diff --git a/Telegram/SourceFiles/gui/flatlabel.cpp b/Telegram/SourceFiles/gui/flatlabel.cpp index 8255ff1c2..9e3676b3b 100644 --- a/Telegram/SourceFiles/gui/flatlabel.cpp +++ b/Telegram/SourceFiles/gui/flatlabel.cpp @@ -60,7 +60,7 @@ void FlatLabel::resizeToWidth(int32 width) { resize(w, h); } -void FlatLabel::setLink(uint16 lnkIndex, const TextLinkPtr &lnk) { +void FlatLabel::setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk) { _text.setLink(lnkIndex, lnk); } @@ -72,30 +72,28 @@ void FlatLabel::mouseMoveEvent(QMouseEvent *e) { void FlatLabel::mousePressEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateHover(); - if (textlnkOver()) { - textlnkDown(textlnkOver()); - update(); - } + ClickHandler::pressed(); } void FlatLabel::mouseReleaseEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateHover(); - if (textlnkOver() && textlnkOver() == textlnkDown()) { - textlnkOver()->onClick(e->button()); + if (ClickHandlerPtr activated = ClickHandler::unpressed()) { + App::activateClickHandler(activated, e->button()); } - textlnkDown(TextLinkPtr()); } void FlatLabel::leaveEvent(QEvent *e) { - if (_myLink) { - if (textlnkOver() == _myLink) { - textlnkOver(TextLinkPtr()); - update(); - } - _myLink = TextLinkPtr(); - setCursor(style::cur_default); - } + ClickHandler::clearActive(this); +} + +void FlatLabel::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { + setCursor(active ? style::cur_pointer : style::cur_default); + update(); +} + +void FlatLabel::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool active) { + update(); } void FlatLabel::updateLink() { @@ -105,17 +103,12 @@ void FlatLabel::updateLink() { void FlatLabel::updateHover() { QPoint m(mapFromGlobal(_lastMousePos)); - bool wasMy = (_myLink == textlnkOver()); + textstyleSet(&_tst); - _myLink = _text.link(m.x(), m.y(), width(), _st.align); + ClickHandlerPtr handler = _text.link(m.x(), m.y(), width(), _st.align); textstyleRestore(); - if (_myLink != textlnkOver()) { - if (wasMy || _myLink || rect().contains(m)) { - textlnkOver(_myLink); - } - setCursor(_myLink ? style::cur_pointer : style::cur_default); - update(); - } + + ClickHandler::setActive(handler, this); } void FlatLabel::setOpacity(float64 o) { diff --git a/Telegram/SourceFiles/gui/flatlabel.h b/Telegram/SourceFiles/gui/flatlabel.h index b06e3bff5..4c3a60571 100644 --- a/Telegram/SourceFiles/gui/flatlabel.h +++ b/Telegram/SourceFiles/gui/flatlabel.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "style.h" -class FlatLabel : public TWidget { +class FlatLabel : public TWidget, public ClickHandlerHost { Q_OBJECT public: @@ -42,7 +42,11 @@ public: void resizeToWidth(int32 width); - void setLink(uint16 lnkIndex, const TextLinkPtr &lnk); + void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk); + + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override; private: @@ -54,6 +58,5 @@ private: float64 _opacity; QPoint _lastMousePos; - TextLinkPtr _myLink; }; diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index de9dbeee6..296409424 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -1045,7 +1045,7 @@ FileLocation::FileLocation(StorageFileType type, const QString &name) : type(typ qint64 s = f.size(); if (s > INT_MAX) { fname = QString(); - _bookmark.reset(0); + _bookmark.clear(); size = 0; type = StorageFileUnknown; } else { @@ -1054,7 +1054,7 @@ FileLocation::FileLocation(StorageFileType type, const QString &name) : type(typ } } else { fname = QString(); - _bookmark.reset(0); + _bookmark.clear(); size = 0; type = StorageFileUnknown; } @@ -1066,7 +1066,7 @@ bool FileLocation::check() const { ReadAccessEnabler enabler(_bookmark); if (enabler.failed()) { - const_cast<FileLocation*>(this)->_bookmark.reset(0); + const_cast<FileLocation*>(this)->_bookmark.clear(); } QFileInfo f(name()); @@ -1087,11 +1087,7 @@ QByteArray FileLocation::bookmark() const { } void FileLocation::setBookmark(const QByteArray &bm) { - if (bm.isEmpty()) { - _bookmark.reset(0); - } else { - _bookmark.reset(new PsFileBookmark(bm)); - } + _bookmark.reset(bm.isEmpty() ? nullptr : new PsFileBookmark(bm)); } bool FileLocation::accessEnable() const { diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index f350a1f3e..63c363e30 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -44,8 +44,6 @@ namespace { const style::textStyle *_textStyle = 0; - TextLinkPtr _overLnk, _downLnk, _zeroLnk; - void _initDefault() { _textStyle = &st::defaultTextStyle; } @@ -59,6 +57,49 @@ namespace { } } +ClickHandlerHost::~ClickHandlerHost() { + ClickHandler::hostDestroyed(this); +} + +ClickHandlerPtr *ClickHandler::_active = nullptr; +ClickHandlerPtr *ClickHandler::_pressed = nullptr; +ClickHandlerHost *ClickHandler::_activeHost = nullptr; +ClickHandlerHost *ClickHandler::_pressedHost = nullptr; + +bool ClickHandler::setActive(const ClickHandlerPtr &p, ClickHandlerHost *host) { + if ((_active && (*_active == p)) || (!_active && !p)) { + return false; + } + + // emit clickHandlerActiveChanged only when there is no + // other pressed click handler currently, if there is + // this method will be called when it is unpressed + if (_active && *_active) { + bool emitClickHandlerActiveChanged = (!_pressed || !*_pressed || *_pressed == *_active); + ClickHandlerPtr wasactive = *_active; + (*_active).clear(); + if (_activeHost) { + if (emitClickHandlerActiveChanged) { + _activeHost->clickHandlerActiveChanged(wasactive, false); + } + _activeHost = nullptr; + } + } + if (p) { + if (!_active) { + _active = new ClickHandlerPtr(); // won't be deleted + } + *_active = p; + if ((_activeHost = host)) { + bool emitClickHandlerActiveChanged = (!_pressed || !*_pressed || *_pressed == *_active); + if (emitClickHandlerActiveChanged) { + _activeHost->clickHandlerActiveChanged(*_active, true); + } + } + } + return true; +} + const QRegularExpression &reDomain() { return _reDomain; } @@ -87,26 +128,6 @@ void textstyleSet(const style::textStyle *style) { _textStyle = style ? style : &st::defaultTextStyle; } -void textlnkOver(const TextLinkPtr &lnk) { - _overLnk = lnk; -} - -const TextLinkPtr &textlnkOver() { - return _overLnk; -} - -void textlnkDown(const TextLinkPtr &lnk) { - _downLnk = lnk; -} - -const TextLinkPtr &textlnkDown() { - return _downLnk; -} - -bool textlnkDrawOver(const TextLinkPtr &lnk) { - return (_overLnk == lnk) && (!_downLnk || _downLnk == lnk); -} - QString textOneLine(const QString &text, bool trim, bool rich) { QString result(text); const QChar *s = text.unicode(), *ch = s, *e = text.unicode() + text.size(); @@ -766,31 +787,31 @@ public: if (_t->_links.size() < lnkIndex) { _t->_links.resize(lnkIndex); const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]); - TextLinkPtr lnk; + ClickHandlerPtr lnk; if (data.fullDisplayed < -4) { // hidden link - lnk = TextLinkPtr(new CustomTextLink(data.url)); + lnk.reset(new HiddenUrlClickHandler(data.url)); } else if (data.fullDisplayed < -3) { // bot command - lnk = TextLinkPtr(new BotCommandLink(data.url)); + lnk.reset(new BotCommandClickHandler(data.url)); } else if (data.fullDisplayed < -2) { // mention if (options.flags & TextTwitterMentions) { - lnk = TextLinkPtr(new TextLink(qsl("https://twitter.com/") + data.url.mid(1), true)); + lnk.reset(new UrlClickHandler(qsl("https://twitter.com/") + data.url.mid(1), true)); } else if (options.flags & TextInstagramMentions) { - lnk = TextLinkPtr(new TextLink(qsl("https://instagram.com/") + data.url.mid(1) + '/', true)); + lnk.reset(new UrlClickHandler(qsl("https://instagram.com/") + data.url.mid(1) + '/', true)); } else { - lnk = TextLinkPtr(new MentionLink(data.url)); + lnk.reset(new MentionClickHandler(data.url)); } } else if (data.fullDisplayed < -1) { // hashtag if (options.flags & TextTwitterMentions) { - lnk = TextLinkPtr(new TextLink(qsl("https://twitter.com/hashtag/") + data.url.mid(1) + qsl("?src=hash"), true)); + lnk.reset(new UrlClickHandler(qsl("https://twitter.com/hashtag/") + data.url.mid(1) + qsl("?src=hash"), true)); } else if (options.flags & TextInstagramMentions) { - lnk = TextLinkPtr(new TextLink(qsl("https://instagram.com/explore/tags/") + data.url.mid(1) + '/', true)); + lnk.reset(new UrlClickHandler(qsl("https://instagram.com/explore/tags/") + data.url.mid(1) + '/', true)); } else { - lnk = TextLinkPtr(new HashtagLink(data.url)); + lnk.reset(new HashtagClickHandler(data.url)); } } else if (data.fullDisplayed < 0) { // email - lnk = TextLinkPtr(new EmailLink(data.url)); + lnk.reset(new EmailClickHandler(data.url)); } else { - lnk = TextLinkPtr(new TextLink(data.url, data.fullDisplayed > 0)); + lnk.reset(new UrlClickHandler(data.url, data.fullDisplayed > 0)); } _t->setLink(lnkIndex, lnk); } @@ -937,78 +958,98 @@ namespace { } } -void TextLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton || button == Qt::MiddleButton) { - PopupTooltip::Hide(); +QString UrlClickHandler::copyToClipboardContextItem() const { + return lang(lng_context_copy_link); +} - QString url = TextLink::encoded(); - QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), QRegularExpression::CaseInsensitiveOption).match(url); - QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url); - QRegularExpressionMatch telegramMeStickers = QRegularExpression(qsl("^https?://telegram\\.me/addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url); - QRegularExpressionMatch telegramMeShareUrl = QRegularExpression(qsl("^https?://telegram\\.me/share/url\\?(.+)$"), QRegularExpression::CaseInsensitiveOption).match(url); - if (telegramMeGroup.hasMatch()) { - url = qsl("tg://join?invite=") + myUrlEncode(telegramMeGroup.captured(1)); - } else if (telegramMeStickers.hasMatch()) { - url = qsl("tg://addstickers?set=") + myUrlEncode(telegramMeStickers.captured(1)); - } else if (telegramMeShareUrl.hasMatch()) { - url = qsl("tg://msg_url?") + telegramMeShareUrl.captured(1); - } else if (telegramMeUser.hasMatch()) { - QString params = url.mid(telegramMeUser.captured(0).size()), postParam; - if (QRegularExpression(qsl("^/\\d+/?(?:\\?|$)")).match(telegramMeUser.captured(2)).hasMatch()) { - postParam = qsl("&post=") + telegramMeUser.captured(3); - } - url = qsl("tg://resolve/?domain=") + myUrlEncode(telegramMeUser.captured(1)) + postParam + (params.isEmpty() ? QString() : '&' + params); - } +void UrlClickHandler::doOpen(QString url) { + PopupTooltip::Hide(); - if (QRegularExpression(qsl("^tg://[a-zA-Z0-9]+"), QRegularExpression::CaseInsensitiveOption).match(url).hasMatch()) { - App::openLocalUrl(url); - } else { - QDesktopServices::openUrl(url); + QRegularExpressionMatch telegramMeUser = QRegularExpression(qsl("^https?://telegram\\.me/([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), QRegularExpression::CaseInsensitiveOption).match(url); + QRegularExpressionMatch telegramMeGroup = QRegularExpression(qsl("^https?://telegram\\.me/joinchat/([a-zA-Z0-9\\.\\_\\-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url); + QRegularExpressionMatch telegramMeStickers = QRegularExpression(qsl("^https?://telegram\\.me/addstickers/([a-zA-Z0-9\\.\\_]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption).match(url); + QRegularExpressionMatch telegramMeShareUrl = QRegularExpression(qsl("^https?://telegram\\.me/share/url\\?(.+)$"), QRegularExpression::CaseInsensitiveOption).match(url); + if (telegramMeGroup.hasMatch()) { + url = qsl("tg://join?invite=") + myUrlEncode(telegramMeGroup.captured(1)); + } else if (telegramMeStickers.hasMatch()) { + url = qsl("tg://addstickers?set=") + myUrlEncode(telegramMeStickers.captured(1)); + } else if (telegramMeShareUrl.hasMatch()) { + url = qsl("tg://msg_url?") + telegramMeShareUrl.captured(1); + } else if (telegramMeUser.hasMatch()) { + QString params = url.mid(telegramMeUser.captured(0).size()), postParam; + if (QRegularExpression(qsl("^/\\d+/?(?:\\?|$)")).match(telegramMeUser.captured(2)).hasMatch()) { + postParam = qsl("&post=") + telegramMeUser.captured(3); } + url = qsl("tg://resolve/?domain=") + myUrlEncode(telegramMeUser.captured(1)) + postParam + (params.isEmpty() ? QString() : '&' + params); + } + + if (QRegularExpression(qsl("^tg://[a-zA-Z0-9]+"), QRegularExpression::CaseInsensitiveOption).match(url).hasMatch()) { + App::openLocalUrl(url); + } else { + QDesktopServices::openUrl(url); } } -void EmailLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton || button == Qt::MiddleButton) { - PopupTooltip::Hide(); - QUrl url(qstr("mailto:") + _email); - if (!QDesktopServices::openUrl(url)) { - psOpenFile(url.toString(QUrl::FullyEncoded), true); - } +QString EmailClickHandler::copyToClipboardContextItem() const { + return lang(lng_context_copy_email); +} + +void EmailClickHandler::doOpen(QString email) { + PopupTooltip::Hide(); + + QUrl url(qstr("mailto:") + email); + if (!QDesktopServices::openUrl(url)) { + psOpenFile(url.toString(QUrl::FullyEncoded), true); } } -void CustomTextLink::onClick(Qt::MouseButton button) const { - Ui::showLayer(new ConfirmLinkBox(text())); +void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const { + Ui::showLayer(new ConfirmLinkBox(url())); } -void LocationLink::onClick(Qt::MouseButton button) const { +QString LocationClickHandler::copyToClipboardContextItem() const { + return lang(lng_context_copy_link); +} + +void LocationClickHandler::onClick(Qt::MouseButton button) const { if (!psLaunchMaps(_coords)) { QDesktopServices::openUrl(_text); } } -void LocationLink::setup() { +void LocationClickHandler::setup() { QString latlon(qsl("%1,%2").arg(_coords.lat).arg(_coords.lon)); _text = qsl("https://maps.google.com/maps?q=") + latlon + qsl("&ll=") + latlon + qsl("&z=16"); } -void MentionLink::onClick(Qt::MouseButton button) const { +QString MentionClickHandler::copyToClipboardContextItem() const { + return lang(lng_context_copy_mention); +} + +void MentionClickHandler::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton || button == Qt::MiddleButton) { App::openPeerByName(_tag.mid(1), ShowAtProfileMsgId); } } -void HashtagLink::onClick(Qt::MouseButton button) const { +QString HashtagClickHandler::copyToClipboardContextItem() const { + return lang(lng_context_copy_hashtag); +} + +void HashtagClickHandler::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton || button == Qt::MiddleButton) { - App::searchByHashtag(_tag, App::mousedItem() ? App::mousedItem()->history()->peer : 0); + App::searchByHashtag(_tag, Ui::getPeerForMouseAction()); } } -void BotCommandLink::onClick(Qt::MouseButton button) const { +void BotCommandClickHandler::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton || button == Qt::MiddleButton) { -// App::insertBotCommand(_cmd); - App::sendBotCommand(_cmd); + if (PeerData *peer = Ui::getPeerForMouseAction()) { + Ui::showPeerHistory(peer, ShowAtTheEndMsgId); + App::sendBotCommand(peer, _cmd); + } else { + App::insertBotCommand(_cmd); + } } } @@ -1291,18 +1332,19 @@ public: draw(left, top, w, align, yFrom, yTo); } - const TextLinkPtr &link(int32 x, int32 y, int32 w, style::align align) { + const ClickHandlerPtr &link(int32 x, int32 y, int32 w, style::align align) { + static const ClickHandlerPtr *zero = new ClickHandlerPtr(); // won't be deleted _lnkX = x; _lnkY = y; - _lnkResult = &_zeroLnk; + _lnkResult = zero; if (!_t->isNull() && _lnkX >= 0 && _lnkX < w && _lnkY >= 0) { draw(0, 0, w, align, _lnkY, _lnkY + 1); } return *_lnkResult; } - void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, int32 w, style::align align, bool breakEverywhere) { - lnk = TextLinkPtr(); + void getState(ClickHandlerPtr &lnk, bool &inText, int32 x, int32 y, int32 w, style::align align, bool breakEverywhere) { + lnk.clear(); inText = false; if (!_t->isNull() && x >= 0 && x < w && y >= 0) { @@ -1335,11 +1377,8 @@ public: return block->color()->p; } if (block->lnkIndex()) { - const TextLinkPtr &l(_t->_links.at(block->lnkIndex() - 1)); - if (l == _overLnk) { - if (l == _downLnk) { - return _textStyle->linkFgDown->p; - } + if (ClickHandler::showAsPressed(_t->_links.at(block->lnkIndex() - 1))) { + return _textStyle->linkFgDown->p; } return _textStyle->linkFg->p; } @@ -1900,15 +1939,14 @@ public: newFont = applyFlags(flags, _t->_font); } if (block->lnkIndex()) { - const TextLinkPtr &l(_t->_links.at(block->lnkIndex() - 1)); - if (l == _overLnk) { - if (l == _downLnk || !_downLnk) { - if (_t->_font != _textStyle->linkFlagsOver) newFont = _textStyle->linkFlagsOver; - } else { - if (_t->_font != _textStyle->linkFlags) newFont = _textStyle->linkFlags; + if (ClickHandler::showAsActive(_t->_links.at(block->lnkIndex() - 1))) { + if (_t->_font != _textStyle->linkFlagsOver) { + newFont = _textStyle->linkFlagsOver; } } else { - if (_t->_font != _textStyle->linkFlags) newFont = _textStyle->linkFlags; + if (_t->_font != _textStyle->linkFlags) { + newFont = _textStyle->linkFlags; + } } } if (newFont != _f) { @@ -2502,7 +2540,7 @@ private: // link and symbol resolve QFixed _lnkX; int32 _lnkY; - const TextLinkPtr *_lnkResult; + const ClickHandlerPtr *_lnkResult; bool *_inTextFlag; uint16 *_getSymbol; bool *_getSymbolAfter, *_getSymbolUpon; @@ -2784,7 +2822,7 @@ void Text::setRichText(style::font font, const QString &text, TextParseOptions o setText(font, parsed, options); } -void Text::setLink(uint16 lnkIndex, const TextLinkPtr &lnk) { +void Text::setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk) { if (!lnkIndex || lnkIndex > _links.size()) return; _links[lnkIndex - 1] = lnk; } @@ -3048,12 +3086,12 @@ void Text::drawElided(QPainter &painter, int32 left, int32 top, int32 w, int32 l p.drawElided(left, top, w, align, lines, yFrom, yTo, removeFromEnd, breakEverywhere); } -const TextLinkPtr &Text::link(int32 x, int32 y, int32 width, style::align align) const { +const ClickHandlerPtr &Text::link(int32 x, int32 y, int32 width, style::align align) const { TextPainter p(0, this); return p.link(x, y, width, align); } -void Text::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, int32 width, style::align align, bool breakEverywhere) const { +void Text::getState(ClickHandlerPtr &lnk, bool &inText, int32 x, int32 y, int32 width, style::align align, bool breakEverywhere) const { TextPainter p(0, this); p.getState(lnk, inText, x, y, width, align, breakEverywhere); } @@ -3102,7 +3140,7 @@ uint32 Text::adjustSelection(uint16 from, uint16 to, TextSelectType selectType) } QString Text::original(uint16 selectedFrom, uint16 selectedTo, ExpandLinksMode mode) const { - QString result; + QString result, emptyurl; result.reserve(_text.size()); int32 lnkFrom = 0, lnkIndex = 0; @@ -3111,14 +3149,14 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, ExpandLinksMode m int32 blockFrom = (i == e) ? _text.size() : (*i)->from(); if (blockLnkIndex != lnkIndex) { if (lnkIndex) { // write link - const TextLinkPtr &lnk(_links.at(lnkIndex - 1)); - const QString &url(lnk ? lnk->text() : QString()); + const ClickHandlerPtr &lnk(_links.at(lnkIndex - 1)); + const QString &url = (mode == ExpandLinksNone || !lnk) ? emptyurl : lnk->text(); int32 rangeFrom = qMax(int32(selectedFrom), lnkFrom), rangeTo = qMin(blockFrom, int32(selectedTo)); if (rangeTo > rangeFrom) { QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom); - if (url.isEmpty() || mode == ExpandLinksNone || lnkFrom != rangeFrom || blockFrom != rangeTo) { + if (url.isEmpty() || lnkFrom != rangeFrom || blockFrom != rangeTo) { result += r; } else { QUrl u(url); @@ -3155,6 +3193,7 @@ QString Text::original(uint16 selectedFrom, uint16 selectedTo, ExpandLinksMode m EntitiesInText Text::originalEntities() const { EntitiesInText result; + QString emptyurl; int32 originalLength = 0, lnkStart = 0, italicStart = 0, boldStart = 0, codeStart = 0, preStart = 0; int32 lnkFrom = 0, lnkIndex = 0, flags = 0; @@ -3187,8 +3226,8 @@ EntitiesInText Text::originalEntities() const { } if (blockLnkIndex != lnkIndex) { if (lnkIndex) { // write link - const TextLinkPtr &lnk(_links.at(lnkIndex - 1)); - const QString &url(lnk ? lnk->text() : QString()); + const ClickHandlerPtr &lnk(_links.at(lnkIndex - 1)); + const QString &url(lnk ? lnk->text() : emptyurl); int32 rangeFrom = lnkFrom, rangeTo = blockFrom; if (rangeTo > rangeFrom) { diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index 7cb1e23a0..5d2307385 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -309,69 +309,199 @@ private: friend class TextPainter; }; -class ITextLink { -public: +class ClickHandler; +using ClickHandlerPtr = QSharedPointer<ClickHandler>; - virtual void onClick(Qt::MouseButton) const = 0; - virtual const QString &text() const { - static const QString _tmp; - return _tmp; +class ClickHandlerHost { +protected: + + virtual void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { } - virtual const QString &readable() const { - static const QString _tmp; - return _tmp; - } - virtual bool fullDisplayed() const { - return true; - } - virtual void setFullDisplayed(bool full) { - } - virtual QString encoded() const { - return QString(); - } - virtual const QLatin1String &type() const = 0; - virtual ~ITextLink() { + virtual void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) { } + virtual ~ClickHandlerHost() = 0; + friend class ClickHandler; }; -#define TEXT_LINK_CLASS(ClassName) public: \ -const QLatin1String &type() const { \ - static const QLatin1String _type(qstr(#ClassName)); \ - return _type; \ -} - -typedef QSharedPointer<ITextLink> TextLinkPtr; - -class TextLink : public ITextLink { - TEXT_LINK_CLASS(TextLink) - +class ClickHandler { public: - TextLink(const QString &url, bool fullDisplayed = true) : _url(url), _fullDisplayed(fullDisplayed) { - QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); - _readable = good.isValid() ? good.toDisplayString() : _url; + virtual void onClick(Qt::MouseButton) const = 0; + + virtual QString tooltip() const { + return QString(); + } + virtual void copyToClipboard() const { + } + virtual QString copyToClipboardContextItem() const { + return QString(); + } + virtual QString text() const { + return QString(); + } + virtual QString dragText() const { + return text(); } - const QString &text() const { - return _url; + virtual ~ClickHandler() { } - void onClick(Qt::MouseButton button) const; + // this method should be called on mouse over a click handler + // it returns true if something was changed or false otherwise + static bool setActive(const ClickHandlerPtr &p, ClickHandlerHost *host = nullptr); - const QString &readable() const { - return _readable; + // this method should be called when mouse leaves the host + // it returns true if something was changed or false otherwise + static bool clearActive(ClickHandlerHost *host = nullptr) { + if (host && _activeHost != host) { + return false; + } + return setActive(ClickHandlerPtr(), host); } - bool fullDisplayed() const { - return _fullDisplayed; + // this method should be called on mouse pressed + static void pressed() { + unpressed(); + if (!_active || !*_active) { + return; + } + if (!_pressed) { + _pressed = new ClickHandlerPtr(); // won't be deleted + } + *_pressed = *_active; + if ((_pressedHost = _activeHost)) { + _pressedHost->clickHandlerPressedChanged(*_pressed, true); + } + } + + // this method should be called on mouse released + // the activated click handler is returned + static ClickHandlerPtr unpressed() { + if (_pressed && *_pressed) { + bool activated = (_active && *_active == *_pressed); + ClickHandlerPtr waspressed = *_pressed; + (*_pressed).clear(); + if (_pressedHost) { + _pressedHost->clickHandlerPressedChanged(waspressed, false); + _pressedHost = nullptr; + } + + if (activated) { + return *_active; + } else if (_active && *_active && _activeHost) { + // emit clickHandlerActiveChanged for current active + // click handler, which we didn't emit while we has + // a pressed click handler + _activeHost->clickHandlerActiveChanged(*_active, true); + } + } + return ClickHandlerPtr(); + } + + static ClickHandlerPtr getActive() { + return _active ? *_active : ClickHandlerPtr(); + } + static ClickHandlerPtr getPressed() { + return _pressed ? *_pressed : ClickHandlerPtr(); + } + + static bool showAsActive(const ClickHandlerPtr &p) { + if (!p || !_active || p != *_active) { + return false; + } + return !_pressed || !*_pressed || (p == *_pressed); + } + static bool showAsPressed(const ClickHandlerPtr &p) { + if (!p || !_active || p != *_active) { + return false; + } + return _pressed && (p == *_pressed); + } + static void hostDestroyed(ClickHandlerHost *host) { + if (_activeHost == host) { + _activeHost = nullptr; + } else if (_pressedHost == host) { + _pressedHost = nullptr; + } + } + +private: + + static ClickHandlerPtr *_active; + static ClickHandlerPtr *_pressed; + static ClickHandlerHost *_activeHost; + static ClickHandlerHost *_pressedHost; + +}; + +class LeftButtonClickHandler : public ClickHandler { +public: + void onClick(Qt::MouseButton button) const override final { + if (button != Qt::LeftButton) return; + onClickImpl(); + } + +protected: + virtual void onClickImpl() const = 0; + +}; + +class TextClickHandler : public ClickHandler { +public: + + TextClickHandler(bool fullDisplayed = true) : _fullDisplayed(fullDisplayed) { + } + + void copyToClipboard() const override { + QString u = url(); + if (!u.isEmpty()) { + QApplication::clipboard()->setText(u); + } + } + + QString tooltip() const override { + return _fullDisplayed ? QString() : readable(); } void setFullDisplayed(bool full) { _fullDisplayed = full; } - QString encoded() const { +protected: + virtual QString url() const = 0; + virtual QString readable() const { + return url(); + } + + bool _fullDisplayed; + +}; + +class UrlClickHandler : public TextClickHandler { +public: + UrlClickHandler(const QString &url, bool fullDisplayed = true) : TextClickHandler(fullDisplayed), _url(url) { + QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); + _readable = good.isValid() ? good.toDisplayString() : _url; + } + QString copyToClipboardContextItem() const override; + + QString text() const override { + return _url; + } + QString dragText() const override { + return url(); + } + + static void doOpen(QString url); + void onClick(Qt::MouseButton button) const override { + if (button == Qt::LeftButton || button == Qt::MiddleButton) { + doOpen(url()); + } + } + +protected: + QString url() const override { QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _url); @@ -380,46 +510,47 @@ public: } return result; } + QString readable() const override { + return _readable; + } private: - QString _url, _readable; - bool _fullDisplayed; + +}; +typedef QSharedPointer<TextClickHandler> TextClickHandlerPtr; + +class HiddenUrlClickHandler : public UrlClickHandler { +public: + HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) { + } + void onClick(Qt::MouseButton button) const override; }; -class CustomTextLink : public TextLink { +class EmailClickHandler : public TextClickHandler { public: - - CustomTextLink(const QString &url) : TextLink(url, false) { + EmailClickHandler(const QString &email) : _email(email) { } - void onClick(Qt::MouseButton button) const; -}; + QString copyToClipboardContextItem() const override; -class EmailLink : public ITextLink { - TEXT_LINK_CLASS(EmailLink) - -public: - - EmailLink(const QString &email) : _email(email) { - } - - const QString &text() const { + QString text() const override { return _email; } - void onClick(Qt::MouseButton button) const; - - const QString &readable() const { - return _email; + static void doOpen(QString email); + void onClick(Qt::MouseButton button) const override { + if (button == Qt::LeftButton || button == Qt::MiddleButton) { + doOpen(_email); + } } - QString encoded() const { +protected: + QString url() const override { return _email; } private: - QString _email; }; @@ -441,26 +572,20 @@ inline uint qHash(const LocationCoords &t, uint seed = 0) { return qHash(QtPrivate::QHashCombine().operator()(qHash(t.lat), t.lon), seed); } -class LocationLink : public ITextLink { - TEXT_LINK_CLASS(LocationLink) - +class LocationClickHandler : public TextClickHandler { public: - - LocationLink(const LocationCoords &coords) : _coords(coords) { + LocationClickHandler(const LocationCoords &coords) : _coords(coords) { setup(); } + QString copyToClipboardContextItem() const override; - const QString &text() const { + QString text() const override { return _text; } + void onClick(Qt::MouseButton button) const override; - void onClick(Qt::MouseButton button) const; - - const QString &readable() const { - return _text; - } - - QString encoded() const { +protected: + QString url() const override { return _text; } @@ -472,86 +597,63 @@ private: }; -class MentionLink : public ITextLink { - TEXT_LINK_CLASS(MentionLink) - +class MentionClickHandler : public TextClickHandler { public: - - MentionLink(const QString &tag) : _tag(tag) { + MentionClickHandler(const QString &tag) : _tag(tag) { } + QString copyToClipboardContextItem() const override; - const QString &text() const { + QString text() const override { return _tag; } + void onClick(Qt::MouseButton button) const override; - void onClick(Qt::MouseButton button) const; - - const QString &readable() const { - return _tag; - } - - QString encoded() const { +protected: + QString url() const override { return _tag; } private: - QString _tag; }; -class HashtagLink : public ITextLink { - TEXT_LINK_CLASS(HashtagLink) - +class HashtagClickHandler : public TextClickHandler { public: - - HashtagLink(const QString &tag) : _tag(tag) { + HashtagClickHandler(const QString &tag) : _tag(tag) { } + QString copyToClipboardContextItem() const override; - const QString &text() const { + QString text() const override { return _tag; } + void onClick(Qt::MouseButton button) const override; - void onClick(Qt::MouseButton button) const; - - const QString &readable() const { - return _tag; - } - - QString encoded() const { +protected: + QString url() const override { return _tag; } private: - QString _tag; }; -class BotCommandLink : public ITextLink { - TEXT_LINK_CLASS(BotCommandLink) - +class BotCommandClickHandler : public TextClickHandler { public: - - BotCommandLink(const QString &cmd) : _cmd(cmd) { + BotCommandClickHandler(const QString &cmd) : _cmd(cmd) { } - - const QString &text() const { + QString text() const override { return _cmd; } + void onClick(Qt::MouseButton button) const override; - void onClick(Qt::MouseButton button) const; - - const QString &readable() const { - return _cmd; - } - - QString encoded() const { +protected: + QString url() const override { return _cmd; } private: - QString _cmd; }; @@ -608,7 +710,7 @@ public: void setRichText(style::font font, const QString &text, TextParseOptions options = _defaultOptions, const TextCustomTagsMap &custom = TextCustomTagsMap()); void setMarkedText(style::font font, const QString &text, const EntitiesInText &entities, const TextParseOptions &options = _defaultOptions); - void setLink(uint16 lnkIndex, const TextLinkPtr &lnk); + void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk); bool hasLinks() const; bool hasSkipBlock() const { @@ -641,12 +743,12 @@ public: drawElided(p, rtl() ? right : (outerw - right - width), top, width, lines, align, yFrom, yTo, removeFromEnd, breakEverywhere); } - const TextLinkPtr &link(int32 x, int32 y, int32 width, style::align align = style::al_left) const; - const TextLinkPtr &linkLeft(int32 x, int32 y, int32 width, int32 outerw, style::align align = style::al_left) const { + const ClickHandlerPtr &link(int32 x, int32 y, int32 width, style::align align = style::al_left) const; + const ClickHandlerPtr &linkLeft(int32 x, int32 y, int32 width, int32 outerw, style::align align = style::al_left) const { return link(rtl() ? (outerw - x - width) : x, y, width, align); } - void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, int32 width, style::align align = style::al_left, bool breakEverywhere = false) const; - void getStateLeft(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, int32 width, int32 outerw, style::align align = style::al_left, bool breakEverywhere = false) const { + void getState(ClickHandlerPtr &lnk, bool &inText, int32 x, int32 y, int32 width, style::align align = style::al_left, bool breakEverywhere = false) const; + void getStateLeft(ClickHandlerPtr &lnk, bool &inText, int32 x, int32 y, int32 width, int32 outerw, style::align align = style::al_left, bool breakEverywhere = false) const { return getState(lnk, inText, rtl() ? (outerw - x - width) : x, y, width, align, breakEverywhere); } void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y, int32 width, style::align align = style::al_left) const; @@ -710,7 +812,7 @@ private: typedef QVector<ITextBlock*> TextBlocks; TextBlocks _blocks; - typedef QVector<TextLinkPtr> TextLinks; + typedef QVector<ClickHandlerPtr> TextLinks; TextLinks _links; Qt::LayoutDirection _startDir; @@ -737,15 +839,6 @@ inline void textstyleRestore() { textstyleSet(0); } -// textlnk -void textlnkOver(const TextLinkPtr &lnk); -const TextLinkPtr &textlnkOver(); - -void textlnkDown(const TextLinkPtr &lnk); -const TextLinkPtr &textlnkDown(); - -bool textlnkDrawOver(const TextLinkPtr &lnk); - // textcmd QString textcmdSkipBlock(ushort w, ushort h); QString textcmdStartLink(ushort lnkIndex); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index b123023be..7d853db86 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1288,7 +1288,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, entities.push_front(EntityInText(EntityInTextItalic, 0, text.size())); result = HistoryMessage::create(this, m.vid.v, m.vflags.v, m.vreply_to_msg_id.v, m.vvia_bot_id.v, date(m.vdate), m.vfrom_id.v, text, entities); } else if (badMedia) { - result = HistoryService::create(this, m.vid.v, date(m.vdate), lang(lng_message_empty), m.vflags.v, nullptr, m.has_from_id() ? m.vfrom_id.v : 0); + result = HistoryService::create(this, m.vid.v, date(m.vdate), lang(lng_message_empty), m.vflags.v, m.has_from_id() ? m.vfrom_id.v : 0); } else { result = HistoryMessage::create(this, m); } @@ -1454,8 +1454,8 @@ HistoryItem *History::createItemPhoto(MsgId id, MTPDmessage::Flags flags, int32 return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, photo, caption); } -HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags, HistoryMedia *media, bool newMsg) { - return addNewItem(HistoryService::create(this, msgId, date, text, flags, media), newMsg); +HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags, bool newMsg) { + return addNewItem(HistoryService::create(this, msgId, date, text, flags), newMsg); } HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type) { @@ -2705,6 +2705,19 @@ void HistoryBlock::removeItem(HistoryItem *item) { } } +void ReplyMarkupClickHandler::onClickImpl() const { + if (HistoryItem *item = App::histItemById(_msgId)) { + if (auto *markup = item->Get<HistoryMessageReplyMarkup>()) { + if (_row < markup->rows.size()) { + const HistoryMessageReplyMarkup::ButtonRow &row(markup->rows.at(_row)); + if (_col < row.size()) { + App::activateBotCommand(item->history()->peer, row.at(_col), _msgId.msg); + } + } + } + } +} + ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s) : _item(item) , _a_selected(animation(this, &ReplyKeyboard::step_selected)) @@ -2718,7 +2731,7 @@ ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s) for (int j = 0; j != s; ++j) { Button &button(newRow[j]); QString str = row.at(j).text; - button.link.reset(new TextLink(qsl("https://telegram.org"))); + button.link.reset(new ReplyMarkupClickHandler(item->fullId(), i, j)); button.text.setText(_st->textFont(), textOneLine(str), _textPlainOptions); button.characters = str.isEmpty() ? 1 : str.size(); } @@ -2793,17 +2806,15 @@ void ReplyKeyboard::paint(Painter &p, const QRect &clip) const { if (rtl()) rect.moveLeft(_width - rect.left() - rect.width()); - bool down = (textlnkDown() == button.link); - float64 howMuchOver = button.howMuchOver; - _st->paintButton(p, rect, button.text, down, howMuchOver); + _st->paintButton(p, rect, button.text, ClickHandler::showAsPressed(button.link), button.howMuchOver); } } } -void ReplyKeyboard::getState(TextLinkPtr &lnk, int x, int y) const { +void ReplyKeyboard::getState(ClickHandlerPtr &lnk, int x, int y) const { t_assert(_width > 0); - lnk.reset(); + lnk.clear(); for_const(const ButtonRow &row, _rows) { for_const(const Button &button, row) { QRect rect(button.rect); @@ -2818,7 +2829,7 @@ void ReplyKeyboard::getState(TextLinkPtr &lnk, int x, int y) const { } } -void ReplyKeyboard::linkOver(const TextLinkPtr &lnk) { +void ReplyKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { /*if (newSel != _sel) { if (newSel < 0) { setCursor(style::cur_default); @@ -2845,7 +2856,7 @@ void ReplyKeyboard::linkOver(const TextLinkPtr &lnk) { }*/ } -void ReplyKeyboard::linkOut(const TextLinkPtr &lnk) { +void ReplyKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { } @@ -2876,8 +2887,8 @@ void ReplyKeyboard::clearSelection() { _a_selected.stop(); } -void ReplyKeyboard::Style::paintButton(Painter &p, const QRect &rect, const Text &text, bool down, float64 howMuchOver) const { - paintButtonBg(p, rect, down, howMuchOver); +void ReplyKeyboard::Style::paintButton(Painter &p, const QRect &rect, const Text &text, bool pressed, float64 howMuchOver) const { + paintButtonBg(p, rect, pressed, howMuchOver); int tx = rect.x(), tw = rect.width(); if (tw > st::botKbFont->elidew + _st->padding * 2) { @@ -2887,7 +2898,7 @@ void ReplyKeyboard::Style::paintButton(Painter &p, const QRect &rect, const Text tx += (tw - st::botKbFont->elidew) / 2; tw = st::botKbFont->elidew; } - int textTop = rect.y() + (down ? _st->downTextTop : _st->textTop); + int textTop = rect.y() + (pressed ? _st->downTextTop : _st->textTop); text.drawElided(p, tx, textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top); } @@ -3006,6 +3017,17 @@ void HistoryMessageDate::paint(Painter &p, int y, int w) const { p.drawText(left + st::msgServicePadding.left(), y + st::msgServiceMargin.top() + st::msgServicePadding.top() + st::msgServiceFont->ascent, _text); } +void HistoryMediaPtr::reset(HistoryItem *host, HistoryMedia *p) { + if (_p) { + _p->detachFromItem(host); + delete _p; + } + _p = p; + if (_p) { + _p->attachToItem(host); + } +} + HistoryItem::HistoryItem(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime msgDate, int32 from) : HistoryElem() , y(0) , id(msgId) @@ -3020,6 +3042,26 @@ void HistoryItem::finishCreate() { App::historyRegItem(this); } +void HistoryItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + if (auto *markup = Get<HistoryMessageReplyMarkup>()) { + if (markup->inlineKeyboard) { + markup->inlineKeyboard->clickHandlerActiveChanged(p, active); + } + } + App::hoveredLinkItem(active ? this : nullptr); + Ui::repaintHistoryItem(this); +} + +void HistoryItem::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + if (auto *markup = Get<HistoryMessageReplyMarkup>()) { + if (markup->inlineKeyboard) { + markup->inlineKeyboard->clickHandlerPressedChanged(p, pressed); + } + } + App::pressedLinkItem(pressed ? this : nullptr); + Ui::repaintHistoryItem(this); +} + void HistoryItem::destroy() { bool wasAtBottom = history()->loadedAtBottom(); _history->removeNotification(this); @@ -3310,25 +3352,27 @@ HistoryFileMedia::HistoryFileMedia() : HistoryMedia() , _animation(0) { } -void HistoryFileMedia::linkOver(HistoryItem *parent, const TextLinkPtr &lnk) { - if ((lnk == _savel || lnk == _cancell) && !dataLoaded()) { - ensureAnimation(parent); - _animation->a_thumbOver.start(1); - _animation->_a_thumbOver.start(); +void HistoryFileMedia::clickHandlerActiveChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool active) { + if (p == _savel || p == _cancell) { + if (active && !dataLoaded()) { + ensureAnimation(parent); + _animation->a_thumbOver.start(1); + _animation->_a_thumbOver.start(); + } else if (!active && _animation) { + _animation->a_thumbOver.start(0); + _animation->_a_thumbOver.start(); + } } } -void HistoryFileMedia::linkOut(HistoryItem *parent, const TextLinkPtr &lnk) { - if (_animation && (lnk == _savel || lnk == _cancell)) { - _animation->a_thumbOver.start(0); - _animation->_a_thumbOver.start(); - } +void HistoryFileMedia::clickHandlerPressedChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool pressed) { + Ui::repaintHistoryItem(parent); } -void HistoryFileMedia::setLinks(ITextLink *openl, ITextLink *savel, ITextLink *cancell) { - _openl.reset(openl); - _savel.reset(savel); - _cancell.reset(cancell); +void HistoryFileMedia::setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell) { + _openl = std_::move(openl); + _savel = std_::move(savel); + _cancell = std_::move(cancell); } void HistoryFileMedia::setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const { @@ -3394,10 +3438,8 @@ HistoryFileMedia::~HistoryFileMedia() { HistoryPhoto::HistoryPhoto(PhotoData *photo, const QString &caption, const HistoryItem *parent) : HistoryFileMedia() , _data(photo) -, _pixw(1) -, _pixh(1) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) { - setLinks(new PhotoLink(_data), new PhotoSaveLink(_data), new PhotoCancelLink(_data)); + setLinks(MakeShared<PhotoOpenClickHandler>(_data), MakeShared<PhotoSaveClickHandler>(_data), MakeShared<PhotoCancelClickHandler>(_data)); if (!caption.isEmpty()) { _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); @@ -3406,10 +3448,8 @@ HistoryPhoto::HistoryPhoto(PhotoData *photo, const QString &caption, const Histo } HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : HistoryFileMedia() -, _data(App::feedPhoto(photo)) -, _pixw(1) -, _pixh(1) { - setLinks(new PhotoLink(_data, chat), new PhotoSaveLink(_data, chat), new PhotoCancelLink(_data)); +, _data(App::feedPhoto(photo)) { + setLinks(MakeShared<PhotoOpenClickHandler>(_data, chat), MakeShared<PhotoSaveClickHandler>(_data, chat), MakeShared<PhotoCancelClickHandler>(_data, chat)); _width = width; init(); @@ -3420,7 +3460,7 @@ HistoryPhoto::HistoryPhoto(const HistoryPhoto &other) : HistoryFileMedia() , _pixw(other._pixw) , _pixh(other._pixh) , _caption(other._caption) { - setLinks(new PhotoLink(_data), new PhotoSaveLink(_data), new PhotoCancelLink(_data)); + setLinks(MakeShared<PhotoOpenClickHandler>(_data), MakeShared<PhotoSaveClickHandler>(_data), MakeShared<PhotoCancelClickHandler>(_data)); init(); } @@ -3568,7 +3608,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); + bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -3612,7 +3652,7 @@ void HistoryPhoto::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } } -void HistoryPhoto::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistoryPhoto::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 skipx = 0, skipy = 0, width = _width, height = _height; bool bubble = parent->hasBubble(); @@ -3734,7 +3774,7 @@ HistoryVideo::HistoryVideo(DocumentData *document, const QString &caption, const _caption.setText(st::msgFont, caption + parent->skipBlock(), itemTextNoMonoOptions(parent)); } - setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data); setStatusSize(FileStatusSizeReady); @@ -3745,7 +3785,7 @@ HistoryVideo::HistoryVideo(const HistoryVideo &other) : HistoryFileMedia() , _data(other._data) , _thumbw(other._thumbw) , _caption(other._caption) { - setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data); setStatusSize(other._statusSize); } @@ -3871,7 +3911,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); + bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -3917,7 +3957,7 @@ void HistoryVideo::draw(Painter &p, const HistoryItem *parent, const QRect &r, b } } -void HistoryVideo::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistoryVideo::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; bool loaded = _data->loaded(); @@ -4035,7 +4075,7 @@ HistoryDocument::HistoryDocument(DocumentData *document, const QString &caption, named->_namew = st::semiboldFont->width(named->_name); } - setLinks(new DocumentOpenLink(_data), _data->voice() ? (ITextLink*)(new VoiceSaveLink(_data)) : new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data); setStatusSize(FileStatusSizeReady); @@ -4060,7 +4100,7 @@ HistoryDocument::HistoryDocument(const HistoryDocument &other) : HistoryFileMedi } } - setLinks(new DocumentOpenLink(_data), _data->voice() ? (ITextLink*)(new VoiceSaveLink(_data)) : new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data); setStatusSize(other._statusSize); @@ -4084,8 +4124,8 @@ void HistoryDocument::createComponents(bool caption) { } UpdateComponents(mask); if (auto *thumbed = Get<HistoryDocumentThumbed>()) { - thumbed->_linksavel.reset(new DocumentSaveLink(_data)); - thumbed->_linkcancell.reset(new DocumentCancelLink(_data)); + thumbed->_linksavel.reset(new DocumentSaveClickHandler(_data)); + thumbed->_linkcancell.reset(new DocumentCancelClickHandler(_data)); } } @@ -4206,7 +4246,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); + bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } p.setOpacity(radialOpacity * p.opacity()); @@ -4233,8 +4273,8 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r } if (_data->status != FileUploadFailed) { - const TextLinkPtr &lnk((_data->loading() || _data->status == FileUploading) ? thumbed->_linkcancell : thumbed->_linksavel); - bool over = textlnkDrawOver(lnk); + const ClickHandlerPtr &lnk((_data->loading() || _data->status == FileUploading) ? thumbed->_linkcancell : thumbed->_linksavel); + bool over = ClickHandler::showAsActive(lnk); p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont); p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg)); p.drawTextLeft(nameleft, linktop, _width, thumbed->_link, thumbed->_linkw); @@ -4254,7 +4294,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r float64 over = _animation->a_thumbOver.current(); p.setBrush(style::interpolate(outbg ? st::msgFileOutBg : st::msgFileInBg, outbg ? st::msgFileOutBgOver : st::msgFileInBgOver, over)); } else { - bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); + bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(outbg ? (over ? st::msgFileOutBgOver : st::msgFileOutBg) : (over ? st::msgFileInBgOver : st::msgFileInBg)); } @@ -4380,7 +4420,7 @@ void HistoryDocument::draw(Painter &p, const HistoryItem *parent, const QRect &r } } -void HistoryDocument::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistoryDocument::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; bool out = parent->out(), isPost = parent->isPost(), outbg = out && !isPost; @@ -4614,7 +4654,7 @@ HistoryGif::HistoryGif(DocumentData *document, const QString &caption, const His , _thumbh(1) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) , _gif(nullptr) { - setLinks(new GifOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data, true); setStatusSize(FileStatusSizeReady); @@ -4632,7 +4672,7 @@ HistoryGif::HistoryGif(const HistoryGif &other) : HistoryFileMedia() , _thumbh(other._thumbh) , _caption(other._caption) , _gif(nullptr) { - setLinks(new GifOpenLink(_data), new GifOpenLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data, true); setStatusSize(other._statusSize); } @@ -4812,7 +4852,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); + bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } p.setOpacity(radialOpacity * p.opacity()); @@ -4861,7 +4901,7 @@ void HistoryGif::draw(Painter &p, const HistoryItem *parent, const QRect &r, boo } } -void HistoryGif::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistoryGif::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 skipx = 0, skipy = 0, width = _width, height = _height; bool bubble = parent->hasBubble(); @@ -5087,7 +5127,7 @@ void HistorySticker::draw(Painter &p, const HistoryItem *parent, const QRect &r, } } -void HistorySticker::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistorySticker::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; bool out = parent->out(), isPost = parent->isPost(), outbg = out && !isPost; @@ -5144,22 +5184,18 @@ void HistorySticker::updateFrom(const MTPMessageMedia &media, HistoryItem *paren } } -void SendMessageLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton) { - Ui::showPeerHistory(peer()->id, ShowAtUnreadMsgId); - } +void SendMessageClickHandler::onClickImpl() const { + Ui::showPeerHistory(peer()->id, ShowAtUnreadMsgId); } -void AddContactLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton) { - if (HistoryItem *item = App::histItemById(peerToChannel(peer()), msgid())) { - if (HistoryMedia *media = item->getMedia()) { - if (media->type() == MediaTypeContact) { - QString fname = static_cast<HistoryContact*>(media)->fname(); - QString lname = static_cast<HistoryContact*>(media)->lname(); - QString phone = static_cast<HistoryContact*>(media)->phone(); - Ui::showLayer(new AddContactBox(fname, lname, phone)); - } +void AddContactClickHandler::onClickImpl() const { + if (HistoryItem *item = App::histItemById(peerToChannel(peer()), msgid())) { + if (HistoryMedia *media = item->getMedia()) { + if (media->type() == MediaTypeContact) { + QString fname = static_cast<HistoryContact*>(media)->fname(); + QString lname = static_cast<HistoryContact*>(media)->lname(); + QString phone = static_cast<HistoryContact*>(media)->phone(); + Ui::showLayer(new AddContactBox(fname, lname, phone)); } } } @@ -5186,10 +5222,10 @@ void HistoryContact::initDimensions(const HistoryItem *parent) { _contact->loadUserpic(); } if (_contact && _contact->contact > 0) { - _linkl.reset(new SendMessageLink(_contact)); + _linkl.reset(new SendMessageClickHandler(_contact)); _link = lang(lng_profile_send_message).toUpper(); } else if (_userId) { - _linkl.reset(new AddContactLink(parent->history()->peer->id, parent->id)); + _linkl.reset(new AddContactClickHandler(parent->history()->peer->id, parent->id)); _link = lang(lng_profile_add_contact).toUpper(); } _linkw = _link.isEmpty() ? 0 : st::semiboldFont->width(_link); @@ -5244,7 +5280,7 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - bool over = textlnkDrawOver(_linkl); + bool over = ClickHandler::showAsActive(_linkl); p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont); p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg)); p.drawTextLeft(nameleft, linktop, width, _link, _linkw); @@ -5269,7 +5305,7 @@ void HistoryContact::draw(Painter &p, const HistoryItem *parent, const QRect &r, p.drawTextLeft(nameleft, statustop, width, _phone); } -void HistoryContact::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistoryContact::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { bool out = parent->out(), isPost = parent->isPost(), outbg = out && !isPost; int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; @@ -5282,7 +5318,7 @@ void HistoryContact::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 } } if (x >= 0 && y >= 0 && x < _width && y < _height && _contact) { - lnk = _contact->lnk; + lnk = _contact->openLink(); return; } } @@ -5378,7 +5414,7 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { } if (!_lineHeight) _lineHeight = qMax(st::webPageTitleFont->height, st::webPageDescriptionFont->height); - if (!_openl && !_data->url.isEmpty()) _openl = TextLinkPtr(new TextLink(_data->url)); + if (!_openl && !_data->url.isEmpty()) _openl.reset(new UrlClickHandler(_data->url, true)); // init layout QString title(_data->title.isEmpty() ? _data->author : _data->title); @@ -5698,7 +5734,7 @@ void HistoryWebPage::draw(Painter &p, const HistoryItem *parent, const QRect &r, } } -void HistoryWebPage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistoryWebPage::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 skipx = 0, skipy = 0, width = _width, height = _height; @@ -5753,15 +5789,15 @@ void HistoryWebPage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 } } -void HistoryWebPage::linkOver(HistoryItem *parent, const TextLinkPtr &lnk) { +void HistoryWebPage::clickHandlerActiveChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool active) { if (_attach) { - _attach->linkOver(parent, lnk); + _attach->clickHandlerActiveChanged(parent, p, active); } } -void HistoryWebPage::linkOut(HistoryItem *parent, const TextLinkPtr &lnk) { +void HistoryWebPage::clickHandlerPressedChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool pressed) { if (_attach) { - _attach->linkOut(parent, lnk); + _attach->clickHandlerPressedChanged(parent, p, pressed); } } @@ -5994,7 +6030,7 @@ _description(st::msgMinWidth) { _description.setText(st::webPageDescriptionFont, textClean(description), _webpageDescriptionOptions); } - _link.reset(new LocationLink(coords)); + _link.reset(new LocationClickHandler(coords)); _data = App::location(coords); } @@ -6134,7 +6170,7 @@ void HistoryLocation::draw(Painter &p, const HistoryItem *parent, const QRect &r } } -void HistoryLocation::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { +void HistoryLocation::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const { if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return; int32 skipx = 0, skipy = 0, width = _width, height = _height; bool bubble = parent->hasBubble(); @@ -6192,14 +6228,14 @@ int32 HistoryLocation::fullHeight() const { return st::locationSize.height(); } -void ViaInlineBotLink::onClick(Qt::MouseButton button) const { +void ViaInlineBotClickHandler::onClickImpl() const { App::insertBotCommand('@' + _bot->username); } void HistoryMessageVia::create(int32 userId) { _bot = App::user(peerFromUser(userId)); _maxWidth = st::msgServiceNameFont->width(lng_inline_bot_via(lt_inline_bot, '@' + _bot->username)); - _lnk.reset(new ViaInlineBotLink(_bot)); + _lnk.reset(new ViaInlineBotClickHandler(_bot)); } void HistoryMessageVia::resize(int32 availw) const { @@ -6254,7 +6290,7 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const { textstyleSet(&st::inFwdTextStyle); _text.setText(st::msgServiceNameFont, text, opts); textstyleRestore(); - _text.setLink(1, (_originalId && _authorOriginal->isChannel()) ? TextLinkPtr(new MessageLink(_authorOriginal->id, _originalId)) : _authorOriginal->lnk); + _text.setLink(1, (_originalId && _authorOriginal->isChannel()) ? ClickHandlerPtr(new GoToMessageClickHandler(_authorOriginal->id, _originalId)) : _authorOriginal->openLink()); if (via) { _text.setLink(2, via->_lnk); } @@ -6278,7 +6314,7 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) { updateName(); - replyToLnk = TextLinkPtr(new MessageLink(replyToMsg->history()->peer->id, replyToMsg->id)); + replyToLnk.reset(new GoToMessageClickHandler(replyToMsg->history()->peer->id, replyToMsg->id)); if (!replyToMsg->Has<HistoryMessageForwarded>()) { if (UserData *bot = replyToMsg->viaBot()) { _replyToVia.reset(new HistoryMessageVia()); @@ -6457,8 +6493,7 @@ HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags fl createComponents(config); if (HistoryMedia *mediaOriginal = fwd->getMedia()) { - _media = mediaOriginal->clone(); - _media->attachToItem(this); + _media.reset(this, mediaOriginal->clone()); } setText(fwd->originalText(), fwd->originalEntities()); } @@ -6482,8 +6517,7 @@ HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags : HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) { createComponentsHelper(flags, replyTo, viaBotId); - _media = new HistoryPhoto(photo, caption, this); - _media->attachToItem(this); + _media.reset(this, new HistoryPhoto(photo, caption, this)); setText(QString(), EntitiesInText()); } @@ -6584,26 +6618,26 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media, QString ¤tTex switch (media ? media->type() : mtpc_messageMediaEmpty) { case mtpc_messageMediaContact: { const MTPDmessageMediaContact &d(media->c_messageMediaContact()); - _media = new HistoryContact(d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number)); + _media.reset(this, new HistoryContact(d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number))); } break; case mtpc_messageMediaGeo: { const MTPGeoPoint &point(media->c_messageMediaGeo().vgeo); if (point.type() == mtpc_geoPoint) { const MTPDgeoPoint &d(point.c_geoPoint()); - _media = new HistoryLocation(LocationCoords(d.vlat.v, d.vlong.v)); + _media.reset(this, new HistoryLocation(LocationCoords(d.vlat.v, d.vlong.v))); } } break; case mtpc_messageMediaVenue: { const MTPDmessageMediaVenue &d(media->c_messageMediaVenue()); if (d.vgeo.type() == mtpc_geoPoint) { const MTPDgeoPoint &g(d.vgeo.c_geoPoint()); - _media = new HistoryLocation(LocationCoords(g.vlat.v, g.vlong.v), qs(d.vtitle), qs(d.vaddress)); + _media.reset(this, new HistoryLocation(LocationCoords(g.vlat.v, g.vlong.v), qs(d.vtitle), qs(d.vaddress))); } } break; case mtpc_messageMediaPhoto: { const MTPDmessageMediaPhoto &photo(media->c_messageMediaPhoto()); if (photo.vphoto.type() == mtpc_photo) { - _media = new HistoryPhoto(App::feedPhoto(photo.vphoto.c_photo()), qs(photo.vcaption), this); + _media.reset(this, new HistoryPhoto(App::feedPhoto(photo.vphoto.c_photo()), qs(photo.vcaption), this)); } } break; case mtpc_messageMediaDocument: { @@ -6617,28 +6651,26 @@ void HistoryMessage::initMedia(const MTPMessageMedia *media, QString ¤tTex switch (d.type()) { case mtpc_webPageEmpty: break; case mtpc_webPagePending: { - _media = new HistoryWebPage(App::feedWebPage(d.c_webPagePending())); + _media.reset(this, new HistoryWebPage(App::feedWebPage(d.c_webPagePending()))); } break; case mtpc_webPage: { - _media = new HistoryWebPage(App::feedWebPage(d.c_webPage())); + _media.reset(this, new HistoryWebPage(App::feedWebPage(d.c_webPage()))); } break; } } break; }; - if (_media) _media->attachToItem(this); } void HistoryMessage::initMediaFromDocument(DocumentData *doc, const QString &caption) { if (doc->sticker()) { - _media = new HistorySticker(doc); + _media.reset(this, new HistorySticker(doc)); } else if (doc->isAnimation()) { - _media = new HistoryGif(doc, caption, this); + _media.reset(this, new HistoryGif(doc, caption, this)); } else if (doc->isVideo()) { - _media = new HistoryVideo(doc, caption, this); + _media.reset(this, new HistoryVideo(doc, caption, this)); } else { - _media = new HistoryDocument(doc, caption, this); + _media.reset(this, new HistoryDocument(doc, caption, this)); } - _media->attachToItem(this); } int32 HistoryMessage::plainMaxWidth() const { @@ -6756,7 +6788,7 @@ int32 HistoryMessage::addToOverview(AddToOverviewMethod method) { if (!indexInOverview()) return 0; int32 result = 0; - if (HistoryMedia *media = getMedia(true)) { + if (HistoryMedia *media = getMedia()) { MediaOverviewType type = mediaToOverviewType(media); if (type != OverviewCount) { if (history()->addToOverview(type, id, method)) { @@ -6773,7 +6805,7 @@ int32 HistoryMessage::addToOverview(AddToOverviewMethod method) { } void HistoryMessage::eraseFromOverview() { - if (HistoryMedia *media = getMedia(true)) { + if (HistoryMedia *media = getMedia()) { MediaOverviewType type = mediaToOverviewType(media); if (type != OverviewCount) { history()->eraseFromOverview(type, id); @@ -6817,8 +6849,8 @@ QString HistoryMessage::inDialogsText() const { return emptyText() ? (_media ? _media->inDialogsText() : QString()) : _text.original(0, 0xFFFF, Text::ExpandLinksNone); } -HistoryMedia *HistoryMessage::getMedia(bool inOverview) const { - return _media; +HistoryMedia *HistoryMessage::getMedia() const { + return _media.data(); } void HistoryMessage::setMedia(const MTPMessageMedia *media) { @@ -6827,10 +6859,7 @@ void HistoryMessage::setMedia(const MTPMessageMedia *media) { bool mediaWasDisplayed = false; if (_media) { mediaWasDisplayed = _media->isDisplayed(); - - _media->detachFromItem(this); - delete _media; - _media = nullptr; + _media.clear(this); } QString t; initMedia(media, t); @@ -7271,8 +7300,8 @@ bool HistoryMessage::pointInTime(int32 right, int32 bottom, int32 x, int32 y, In return QRect(dateX, dateY, HistoryMessage::timeWidth(), st::msgDateFont->height).contains(x, y); } -void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { - lnk = TextLinkPtr(); +void HistoryMessage::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { + lnk.clear(); state = HistoryDefaultCursorState; int left = 0, width = 0, height = _height; @@ -7300,7 +7329,7 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 if (displayFromName()) { if (y >= trect.top() && y < trect.top() + st::msgNameFont->height) { if (x >= trect.left() && x < trect.left() + trect.width() && x < trect.left() + author()->nameText.maxWidth()) { - lnk = author()->lnk; + lnk = author()->openLink(); return; } if (via && !fwd && x >= trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew && x < trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew + via->_width) { @@ -7345,7 +7374,6 @@ void HistoryMessage::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 bool inDate = false; - TextLinkPtr medialnk; if (_media && _media->isDisplayed()) { if (!_media->customInfoLayout()) { inDate = HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault); @@ -7458,17 +7486,14 @@ bool HistoryMessage::hasFromPhoto() const { } HistoryMessage::~HistoryMessage() { - if (_media) { - _media->detachFromItem(this); - deleteAndMark(_media); - } + _media.clear(this); if (auto *reply = Get<HistoryMessageReply>()) { reply->clearData(this); } } void HistoryService::setMessageByAction(const MTPmessageAction &action) { - QList<TextLinkPtr> links; + QList<ClickHandlerPtr> links; LangString text = lang(lng_message_empty); QString from = textcmdLink(1, _from->name); @@ -7488,7 +7513,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { if (u == _from) { text = lng_action_user_joined(lt_from, from); } else { - links.push_back(TextLinkPtr(new PeerLink(u))); + links.push_back(MakeShared<PeerOpenClickHandler>(u)); text = lng_action_add_user(lt_from, from, lt_user, textcmdLink(2, u->name)); } } else if (v.isEmpty()) { @@ -7504,7 +7529,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { } else { text = lng_action_add_users_and_last(lt_accumulated, text, lt_user, linkText); } - links.push_back(TextLinkPtr(new PeerLink(u))); + links.push_back(MakeShared<PeerOpenClickHandler>(u)); } text = lng_action_add_users_many(lt_from, from, lt_users, text); } @@ -7517,13 +7542,13 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { case mtpc_messageActionChatJoinedByLink: { const MTPDmessageActionChatJoinedByLink &d(action.c_messageActionChatJoinedByLink()); - if (true || peerFromUser(d.vinviter_id) == _from->id) { + //if (true || peerFromUser(d.vinviter_id) == _from->id) { text = lng_action_user_joined_by_link(lt_from, from); //} else { - //UserData *u = App::user(App::peerFromUser(d.vinviter_id)); - //second = TextLinkPtr(new PeerLink(u)); - //text = lng_action_user_joined_by_link_from(lt_from, from, lt_inviter, textcmdLink(2, u->name)); - } + // UserData *u = App::user(App::peerFromUser(d.vinviter_id)); + // links.push_back(MakeShared<PeerOpenClickHandler>(u)); + // text = lng_action_user_joined_by_link_from(lt_from, from, lt_inviter, textcmdLink(2, u->name)); + //} if (_from->isSelf() && history()->peer->isMegagroup()) { history()->peer->asChannel()->mgInfo->joinedMessageFound = true; } @@ -7553,7 +7578,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { text = lng_action_user_left(lt_from, from); } else { UserData *u = App::user(peerFromUser(d.vuser_id)); - links.push_back(TextLinkPtr(new PeerLink(u))); + links.push_back(MakeShared<PeerOpenClickHandler>(u)); text = lng_action_kick_user(lt_from, from, lt_user, textcmdLink(2, u->name)); } } break; @@ -7561,8 +7586,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { case mtpc_messageActionChatEditPhoto: { const MTPDmessageActionChatEditPhoto &d(action.c_messageActionChatEditPhoto()); if (d.vphoto.type() == mtpc_photo) { - _media = new HistoryPhoto(history()->peer, d.vphoto.c_photo(), st::msgServicePhotoWidth); - _media->attachToItem(this); + _media.reset(this, new HistoryPhoto(history()->peer, d.vphoto.c_photo(), st::msgServicePhotoWidth)); } text = isPost() ? lang(lng_action_changed_photo_channel) : lng_action_changed_photo(lt_from, from); } break; @@ -7608,7 +7632,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { _text.setText(st::msgServiceFont, text, _historySrvOptions); textstyleRestore(); if (!from.isEmpty()) { - _text.setLink(1, TextLinkPtr(new PeerLink(_from))); + _text.setLink(1, MakeShared<PeerOpenClickHandler>(_from)); } for (int32 i = 0, l = links.size(); i < l; ++i) { _text.setLink(i + 2, links.at(i)); @@ -7626,7 +7650,7 @@ bool HistoryService::updatePinned(bool force) { } if (!pinned->lnk) { - pinned->lnk = TextLinkPtr(new MessageLink(history()->peer->id, pinned->msgId)); + pinned->lnk.reset(new GoToMessageClickHandler(history()->peer->id, pinned->msgId)); } bool gotDependencyItem = false; if (!pinned->msg) { @@ -7663,7 +7687,7 @@ bool HistoryService::updatePinnedText(const QString *pfrom, QString *ptext) { from = textcmdLink(1, _from->name); } - TextLinkPtr second; + ClickHandlerPtr second; auto *pinned = Get<HistoryServicePinned>(); if (pinned && pinned->msg) { HistoryMedia *media = pinned->msg->getMedia(); @@ -7711,7 +7735,7 @@ bool HistoryService::updatePinnedText(const QString *pfrom, QString *ptext) { *ptext = text; } else { setServiceText(text); - _text.setLink(1, TextLinkPtr(new PeerLink(_from))); + _text.setLink(1, MakeShared<PeerOpenClickHandler>(_from)); if (second) { _text.setLink(2, second); } @@ -7738,10 +7762,9 @@ HistoryService::HistoryService(History *history, const MTPDmessageService &msg) setMessageByAction(msg.vaction); } -HistoryService::HistoryService(History *history, MsgId msgId, QDateTime date, const QString &msg, MTPDmessage::Flags flags, HistoryMedia *media, int32 from) : - HistoryItem(history, msgId, flags, date, from) -, _text(st::msgServiceFont, msg, _historySrvOptions, st::dlgMinWidth) -, _media(media) { +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); } void HistoryService::initDimensions() { @@ -7902,8 +7925,8 @@ bool HistoryService::hasPoint(int32 x, int32 y) const { return QRect(left, st::msgServiceMargin.top(), width, height).contains(x, y); } -void HistoryService::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { - lnk = TextLinkPtr(); +void HistoryService::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { + lnk.clear(); state = HistoryDefaultCursorState; int left = 0, width = 0, height = _height - st::msgServiceMargin.top() - st::msgServiceMargin.bottom(); // two small margins @@ -7979,8 +8002,8 @@ QString HistoryService::notificationText() const { return msg; } -HistoryMedia *HistoryService::getMedia(bool inOverview) const { - return inOverview ? 0 : _media; +HistoryMedia *HistoryService::getMedia() const { + return _media.data(); } HistoryService::~HistoryService() { @@ -7989,10 +8012,7 @@ HistoryService::~HistoryService() { App::historyUnregDependency(this, pinned->msg); } } - if (_media) { - _media->detachFromItem(this); - deleteAndMark(_media); - } + _media.clear(this); } HistoryGroup::HistoryGroup(History *history, const MTPDmessageGroup &group, const QDateTime &date) @@ -8000,7 +8020,7 @@ HistoryGroup::HistoryGroup(History *history, const MTPDmessageGroup &group, cons , _minId(group.vmin_id.v) , _maxId(group.vmax_id.v) , _count(group.vcount.v) - , _lnk(new CommentsLink(this)) { + , _lnk(new CommentsClickHandler(this)) { } HistoryGroup::HistoryGroup(History *history, HistoryItem *newItem, const QDateTime &date) @@ -8008,11 +8028,11 @@ HistoryGroup::HistoryGroup(History *history, HistoryItem *newItem, const QDateTi , _minId(newItem->id - 1) , _maxId(newItem->id + 1) , _count(1) - , _lnk(new CommentsLink(this)) { + , _lnk(new CommentsClickHandler(this)) { } -void HistoryGroup::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { - lnk = TextLinkPtr(); +void HistoryGroup::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { + lnk.clear(); state = HistoryDefaultCursorState; int32 left = 0, width = 0, height = _height - st::msgServiceMargin.top() - st::msgServiceMargin.bottom(); // two small margins @@ -8081,8 +8101,8 @@ HistoryCollapse::HistoryCollapse(History *history, MsgId wasMinId, const QDateTi void HistoryCollapse::draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const { } -void HistoryCollapse::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { - lnk = TextLinkPtr(); +void HistoryCollapse::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { + lnk.clear(); state = HistoryDefaultCursorState; } @@ -8093,7 +8113,23 @@ HistoryJoined::HistoryJoined(History *history, const QDateTime &inviteDate, User _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, TextLinkPtr(new PeerLink(inviter))); + _text.setLink(1, MakeShared<PeerOpenClickHandler>(inviter)); } textstyleRestore(); } + +void GoToMessageClickHandler::onClickImpl() const { + if (App::main()) { + HistoryItem *current = App::mousedItem(); + if (current && current->history()->peer->id == peer()) { + App::main()->pushReplyReturn(current); + } + Ui::showPeerHistory(peer(), msgid()); + } +} + +void CommentsClickHandler::onClickImpl() const { + if (App::main() && peerIsChannel(peer())) { + Ui::showPeerHistory(peer(), msgid()); + } +} diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 17b643604..080fe8392 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -242,7 +242,7 @@ public: virtual ~History(); - HistoryItem *addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags = 0, HistoryMedia *media = 0, bool newMsg = true); + HistoryItem *addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags = 0, bool newMsg = true); HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type); HistoryItem *addToHistory(const MTPMessage &msg); HistoryItem *addNewForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *item); @@ -973,7 +973,7 @@ struct HistoryMessageVia : public BaseComponent<HistoryMessageVia> { mutable QString _text; mutable int _width = 0; mutable int _maxWidth = 0; - TextLinkPtr _lnk; + ClickHandlerPtr _lnk; }; struct HistoryMessageViews : public BaseComponent<HistoryMessageViews> { @@ -1037,13 +1037,13 @@ struct HistoryMessageReply : public BaseComponent<HistoryMessageReply> { int replyToWidth() const { return _maxReplyWidth; } - TextLinkPtr replyToLink() const { + ClickHandlerPtr replyToLink() const { return replyToLnk; } MsgId replyToMsgId = 0; HistoryItem *replyToMsg = nullptr; - TextLinkPtr replyToLnk; + ClickHandlerPtr replyToLnk; mutable Text replyToName, replyToText; mutable int replyToVersion = 0; mutable int _maxReplyWidth = 0; @@ -1052,7 +1052,21 @@ struct HistoryMessageReply : public BaseComponent<HistoryMessageReply> { }; Q_DECLARE_OPERATORS_FOR_FLAGS(HistoryMessageReply::PaintFlags); -class ReplyKeyboard final { +class ReplyMarkupClickHandler : public LeftButtonClickHandler { +public: + ReplyMarkupClickHandler(const FullMsgId &msgId, int row, int col) : _msgId(msgId), _row(row), _col(col) { + } + +protected: + void onClickImpl() const override; + +private: + FullMsgId _msgId; + int _row, _col; + +}; + +class ReplyKeyboard { public: class Style { public: @@ -1062,7 +1076,7 @@ public: virtual void startPaint(Painter &p) const = 0; virtual style::font textFont() const = 0; - void paintButton(Painter &p, const QRect &rect, const Text &text, bool down, float64 howMuchOver) const; + void paintButton(Painter &p, const QRect &rect, const Text &text, bool pressed, float64 howMuchOver) const; int buttonSkip() const { return _st->margin; @@ -1077,7 +1091,7 @@ public: virtual void repaint(const HistoryItem *item) const = 0; protected: - virtual void paintButtonBg(Painter &p, const QRect &rect, bool down, float64 howMuchOver) const = 0; + virtual void paintButtonBg(Painter &p, const QRect &rect, bool pressed, float64 howMuchOver) const = 0; private: const style::botKeyboardButton *_st; @@ -1095,10 +1109,11 @@ public: int naturalHeight() const; void paint(Painter &p, const QRect &clip) const; - void getState(TextLinkPtr &lnk, int x, int y) const; + void getState(ClickHandlerPtr &lnk, int x, int y) const; + + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active); + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed); - void linkOver(const TextLinkPtr &lnk); - void linkOut(const TextLinkPtr &lnk); void clearSelection(); private: @@ -1111,7 +1126,7 @@ private: int characters = 0; float64 howMuchOver = 0.; bool full = true; - TextLinkPtr link; + ClickHandlerPtr link; }; using ButtonRow = QVector<Button>; using ButtonRows = QVector<ButtonRow>; @@ -1195,8 +1210,45 @@ struct HistoryMessageUnreadBar : public BaseComponent<HistoryMessageUnreadBar> { bool _freezed = false; }; +// HistoryMedia has a special owning smart pointer +// which regs/unregs this media to the holding HistoryItem class HistoryMedia; -class HistoryItem : public HistoryElem, public Composer { +class HistoryMediaPtr { +public: + HistoryMediaPtr() = default; + HistoryMediaPtr(const HistoryMediaPtr &other) = delete; + HistoryMediaPtr &operator=(const HistoryMediaPtr &other) = delete; + HistoryMedia *data() const { + return _p; + } + void reset(HistoryItem *host, HistoryMedia *p = nullptr); + bool isNull() const { + return data() == nullptr; + } + + void clear(HistoryItem *host) { + reset(host); + } + HistoryMedia *operator->() const { + return data(); + } + HistoryMedia &operator*() const { + t_assert(!isNull()); + return *data(); + } + explicit operator bool() const { + return !isNull(); + } + ~HistoryMediaPtr() { + t_assert(isNull()); + } + +private: + HistoryMedia *_p; + +}; + +class HistoryItem : public HistoryElem, public Composer, public ClickHandlerHost { public: HistoryItem(const HistoryItem &) = delete; @@ -1357,8 +1409,8 @@ public: virtual bool hasPoint(int32 x, int32 y) const { return false; } - virtual void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { - lnk = TextLinkPtr(); + virtual void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const { + lnk.clear(); state = HistoryDefaultCursorState; } virtual void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { // from text @@ -1369,20 +1421,11 @@ public: virtual uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const { return (from << 16) | to; } - virtual void linkOver(const TextLinkPtr &lnk) { - if (auto *markup = Get<HistoryMessageReplyMarkup>()) { - if (markup->inlineKeyboard) { - markup->inlineKeyboard->linkOver(lnk); - } - } - } - virtual void linkOut(const TextLinkPtr &lnk) { - if (auto *markup = Get<HistoryMessageReplyMarkup>()) { - if (markup->inlineKeyboard) { - markup->inlineKeyboard->linkOut(lnk); - } - } - } + + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; + virtual HistoryItemType type() const { return HistoryItemMsg; } @@ -1463,8 +1506,8 @@ public: return FullMsgId(channelId(), id); } - virtual HistoryMedia *getMedia(bool inOverview = false) const { - return 0; + virtual HistoryMedia *getMedia() const { + return nullptr; } virtual void setText(const QString &text, const EntitiesInText &links) { } @@ -1478,13 +1521,13 @@ public: return false; } - virtual int32 infoWidth() const { + virtual int infoWidth() const { return 0; } - virtual int32 timeLeft() const { + virtual int timeLeft() const { return 0; } - virtual int32 timeWidth() const { + virtual int timeWidth() const { return 0; } virtual bool pointInTime(int32 right, int32 bottom, int32 x, int32 y, InfoDisplayType type) const { @@ -1502,10 +1545,10 @@ public: } virtual HistoryMessage *toHistoryMessage() { // dynamic_cast optimize - return 0; + return nullptr; } virtual const HistoryMessage *toHistoryMessage() const { // dynamic_cast optimize - return 0; + return nullptr; } MsgId replyToId() const { if (auto *reply = Get<HistoryMessageReply>()) { @@ -1659,6 +1702,11 @@ protected: return const_cast<ReplyKeyboard*>(static_cast<const HistoryItem*>(this)->inlineReplyKeyboard()); } + Text _text = { int(st::msgMinWidth) }; + int32 _textWidth, _textHeight; + + HistoryMediaPtr _media; + }; // make all the constructors in HistoryItem children protected @@ -1676,15 +1724,12 @@ public: } }; -class MessageLink : public ITextLink { - TEXT_LINK_CLASS(MessageLink) - +class MessageClickHandler : public LeftButtonClickHandler { public: - MessageLink(PeerId peer, MsgId msgid) : _peer(peer), _msgid(msgid) { + MessageClickHandler(PeerId peer, MsgId msgid) : _peer(peer), _msgid(msgid) { } - MessageLink(HistoryItem *item) : _peer(item->history()->peer->id), _msgid(item->id) { + MessageClickHandler(HistoryItem *item) : _peer(item->history()->peer->id), _msgid(item->id) { } - void onClick(Qt::MouseButton button) const; PeerId peer() const { return _peer; } @@ -1695,18 +1740,21 @@ public: private: PeerId _peer; MsgId _msgid; + }; -class CommentsLink : public ITextLink { - TEXT_LINK_CLASS(CommentsLink) - +class GoToMessageClickHandler : public MessageClickHandler { public: - CommentsLink(HistoryItem *item) : _item(item) { - } - void onClick(Qt::MouseButton button) const; + using MessageClickHandler::MessageClickHandler; +protected: + void onClickImpl() const override; +}; -private: - HistoryItem *_item; +class CommentsClickHandler : public MessageClickHandler { +public: + using MessageClickHandler::MessageClickHandler; +protected: + void onClickImpl() const override; }; class RadialAnimation { @@ -1766,11 +1814,23 @@ public: return _height; } virtual void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const = 0; - virtual void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const = 0; + virtual void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const = 0; - virtual void linkOver(HistoryItem *parent, const TextLinkPtr &lnk) { + // if we are in selecting items mode perhaps we want to + // toggle selection instead of activating the pressed link + virtual bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const = 0; + + // if we press and drag on this media should we drag the item + virtual bool dragItem() const { + return false; } - virtual void linkOut(HistoryItem *parent, const TextLinkPtr &lnk) { + + // if we press and drag this link should we drag the item + virtual bool dragItemByHandler(const ClickHandlerPtr &p) const = 0; + + virtual void clickHandlerActiveChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool active) { + } + virtual void clickHandlerPressedChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool pressed) { } virtual bool uploading() const { @@ -1860,15 +1920,38 @@ public: HistoryFileMedia(); - void linkOver(HistoryItem *parent, const TextLinkPtr &lnk); - void linkOut(HistoryItem *parent, const TextLinkPtr &lnk); + bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + return p == _openl || p == _savel || p == _cancell; + } + bool dragItemByHandler(const ClickHandlerPtr &p) const override { + return p == _openl || p == _savel || p == _cancell; + } + + void clickHandlerActiveChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool pressed) override; ~HistoryFileMedia(); protected: - TextLinkPtr _openl, _savel, _cancell; - void setLinks(ITextLink *openl, ITextLink *savel, ITextLink *cancell); + ClickHandlerPtr _openl, _savel, _cancell; + void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell); + void setDocumentLinks(DocumentData *document, bool inlinegif = false) { + ClickHandlerPtr open, save; + if (inlinegif) { + open.reset(new GifOpenClickHandler(document)); + } else { + open.reset(new DocumentOpenClickHandler(document)); + } + if (inlinegif) { + save.reset(new GifOpenClickHandler(document)); + } else if (document->voice()) { + save.reset(new DocumentOpenClickHandler(document)); + } else { + save.reset(new DocumentSaveClickHandler(document)); + } + setLinks(std_::move(open), std_::move(save), MakeShared<DocumentCancelClickHandler>(document)); + } // >= 0 will contain download / upload string, _statusSize = loaded bytes // < 0 will contain played string, _statusSize = -(seconds + 1) played @@ -1940,7 +2023,7 @@ public: int32 resize(int32 width, const HistoryItem *parent) override; void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; const QString inDialogsText() const override; const QString inHistoryText() const override; @@ -1986,7 +2069,8 @@ protected: private: PhotoData *_data; - int16 _pixw, _pixh; + int16 _pixw = 1; + int16 _pixh = 1; Text _caption; }; @@ -2007,7 +2091,7 @@ public: int32 resize(int32 width, const HistoryItem *parent) override; void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; const QString inDialogsText() const override; const QString inHistoryText() const override; @@ -2064,7 +2148,7 @@ private: }; struct HistoryDocumentThumbed : public BaseComponent<HistoryDocumentThumbed> { - TextLinkPtr _linksavel, _linkcancell; + ClickHandlerPtr _linksavel, _linkcancell; int _thumbw = 0; mutable int _linkw = 0; @@ -2114,7 +2198,7 @@ public: int32 resize(int32 width, const HistoryItem *parent) override; void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; const QString inDialogsText() const override; const QString inHistoryText() const override; @@ -2197,7 +2281,7 @@ public: int32 resize(int32 width, const HistoryItem *parent) override; void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; const QString inDialogsText() const override; const QString inHistoryText() const override; @@ -2282,7 +2366,17 @@ public: int32 resize(int32 width, const HistoryItem *parent) override; void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + + bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + return true; + } + bool dragItem() const override { + return true; + } + bool dragItemByHandler(const ClickHandlerPtr &p) const override { + return true; + } const QString inDialogsText() const override; const QString inHistoryText() const override; @@ -2311,24 +2405,18 @@ private: }; -class SendMessageLink : public PeerLink { - TEXT_LINK_CLASS(SendMessageLink) - +class SendMessageClickHandler : public PeerClickHandler { public: - SendMessageLink(PeerData *peer) : PeerLink(peer) { - } - void onClick(Qt::MouseButton button) const; - + using PeerClickHandler::PeerClickHandler; +protected: + void onClickImpl() const override; }; -class AddContactLink : public MessageLink { - TEXT_LINK_CLASS(AddContactLink) - +class AddContactClickHandler : public MessageClickHandler { public: - AddContactLink(PeerId peer, MsgId msgid) : MessageLink(peer, msgid) { - } - void onClick(Qt::MouseButton button) const; - + using MessageClickHandler::MessageClickHandler; +protected: + void onClickImpl() const override; }; class HistoryContact : public HistoryMedia { @@ -2345,7 +2433,14 @@ public: void initDimensions(const HistoryItem *parent) override; void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + + bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + return true; + } + bool dragItemByHandler(const ClickHandlerPtr &p) const override { + return true; + } const QString inDialogsText() const override; const QString inHistoryText() const override; @@ -2381,7 +2476,7 @@ private: QString _fname, _lname, _phone; Text _name; - TextLinkPtr _linkl; + ClickHandlerPtr _linkl; int32 _linkw; QString _link; }; @@ -2402,13 +2497,20 @@ public: int32 resize(int32 width, const HistoryItem *parent) override; void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const override; + + bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + return _attach && _attach->toggleSelectionByHandlerClick(p); + } + bool dragItemByHandler(const ClickHandlerPtr &p) const override { + return _attach && _attach->dragItemByHandler(p); + } const QString inDialogsText() const override; const QString inHistoryText() const override; - void linkOver(HistoryItem *parent, const TextLinkPtr &lnk) override; - void linkOut(HistoryItem *parent, const TextLinkPtr &lnk) override; + void clickHandlerActiveChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(HistoryItem *parent, const ClickHandlerPtr &p, bool pressed) override; bool isDisplayed() const override { return !_data->pendingTill; @@ -2453,7 +2555,7 @@ public: private: WebPageData *_data; - TextLinkPtr _openl; + ClickHandlerPtr _openl; HistoryMedia *_attach; bool _asArticle; @@ -2526,7 +2628,14 @@ public: int32 resize(int32 width, const HistoryItem *parent); void draw(Painter &p, const HistoryItem *parent, const QRect &r, bool selected, uint64 ms) const; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y, const HistoryItem *parent) const; + + bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { + return p == _link; + } + bool dragItemByHandler(const ClickHandlerPtr &p) const override { + return p == _link; + } const QString inDialogsText() const; const QString inHistoryText() const; @@ -2545,20 +2654,20 @@ public: private: LocationData *_data; Text _title, _description; - TextLinkPtr _link; + ClickHandlerPtr _link; int32 fullWidth() const; int32 fullHeight() const; }; -class ViaInlineBotLink : public ITextLink { - TEXT_LINK_CLASS(ViaInlineBotLink) - +class ViaInlineBotClickHandler : public LeftButtonClickHandler { public: - ViaInlineBotLink(UserData *bot) : _bot(bot) { + ViaInlineBotClickHandler(UserData *bot) : _bot(bot) { } - void onClick(Qt::MouseButton button) const; + +protected: + void onClickImpl() const override; private: UserData *_bot; @@ -2623,19 +2732,21 @@ public: bool hasPoint(int32 x, int32 y) const override; bool pointInTime(int32 right, int32 bottom, int32 x, int32 y, InfoDisplayType type) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const override; void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const override; uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const override { return _text.adjustSelection(from, to, type); } - void linkOver(const TextLinkPtr &lnk) override { - if (_media) _media->linkOver(this, lnk); - HistoryItem::linkOver(lnk); + + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override { + if (_media) _media->clickHandlerActiveChanged(this, p, active); + HistoryItem::clickHandlerActiveChanged(p, active); } - void linkOut(const TextLinkPtr &lnk) override { - if (_media) _media->linkOut(this, lnk); - HistoryItem::linkOut(lnk); + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override { + if (_media) _media->clickHandlerActiveChanged(this, p, pressed); + HistoryItem::clickHandlerPressedChanged(p, pressed); } void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const override; @@ -2655,7 +2766,7 @@ public: QString selectedText(uint32 selection) const override; QString inDialogsText() const override; - HistoryMedia *getMedia(bool inOverview = false) const override; + HistoryMedia *getMedia() const override; void setMedia(const MTPMessageMedia *media); void setText(const QString &text, const EntitiesInText &entities) override; QString originalText() const override; @@ -2743,12 +2854,6 @@ protected: // this method draws "via @bot" if it is not painted in forwarded info or in from name void paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const; - Text _text = { int(st::msgMinWidth) }; - - int _textWidth = 0; - int _textHeight = 0; - - HistoryMedia *_media = nullptr; QString _timeText; int _timeWidth = 0; @@ -2812,7 +2917,7 @@ inline MTPDmessage::Flags newForwardedFlags(PeerData *p, int32 from, HistoryMess struct HistoryServicePinned : public BaseComponent<HistoryServicePinned> { MsgId msgId = 0; HistoryItem *msg = nullptr; - TextLinkPtr lnk; + ClickHandlerPtr lnk; }; class HistoryService : public HistoryItem, private HistoryItemInstantiated<HistoryService> { @@ -2821,8 +2926,8 @@ public: static HistoryService *create(History *history, const MTPDmessageService &msg) { return _create(history, msg); } - static HistoryService *create(History *history, MsgId msgId, QDateTime date, const QString &msg, MTPDmessage::Flags flags = 0, HistoryMedia *media = 0, int32 from = 0) { - return _create(history, msgId, date, msg, flags, media, from); + static HistoryService *create(History *history, MsgId msgId, QDateTime date, const QString &msg, MTPDmessage::Flags flags = 0, int32 from = 0) { + return _create(history, msgId, date, msg, flags, from); } bool updateDependencyItem() override { @@ -2845,19 +2950,19 @@ public: void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const override; bool hasPoint(int32 x, int32 y) const override; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const override; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const override; void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const override; uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const override { return _text.adjustSelection(from, to, type); } - void linkOver(const TextLinkPtr &lnk) override { - if (_media) _media->linkOver(this, lnk); - HistoryItem::linkOver(lnk); + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override { + if (_media) _media->clickHandlerActiveChanged(this, p, active); + HistoryItem::clickHandlerActiveChanged(p, active); } - void linkOut(const TextLinkPtr &lnk) override { - if (_media) _media->linkOut(this, lnk); - HistoryItem::linkOut(lnk); + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override { + if (_media) _media->clickHandlerPressedChanged(this, p, pressed); + HistoryItem::clickHandlerPressedChanged(p, pressed); } void drawInDialog(Painter &p, const QRect &r, bool act, const HistoryItem *&cacheFor, Text &cache) const override; @@ -2873,7 +2978,7 @@ public: QString inDialogsText() const override; QString inReplyText() const override; - HistoryMedia *getMedia(bool inOverview = false) const override; + HistoryMedia *getMedia() const override; void setServiceText(const QString &text); @@ -2882,7 +2987,7 @@ public: protected: HistoryService(History *history, const MTPDmessageService &msg); - HistoryService(History *history, MsgId msgId, QDateTime date, const QString &msg, MTPDmessage::Flags flags = 0, HistoryMedia *media = 0, int32 from = 0); + HistoryService(History *history, MsgId msgId, QDateTime date, const QString &msg, MTPDmessage::Flags flags = 0, int32 from = 0); friend class HistoryItemInstantiated<HistoryService>; void initDimensions() override; @@ -2892,10 +2997,6 @@ protected: bool updatePinned(bool force = false); bool updatePinnedText(const QString *pfrom = nullptr, QString *ptext = nullptr); - Text _text = { int(st::msgMinWidth) }; - HistoryMedia *_media = nullptr; - - int32 _textWidth, _textHeight; }; class HistoryGroup : public HistoryService, private HistoryItemInstantiated<HistoryGroup> { @@ -2908,7 +3009,7 @@ public: return _create(history, newItem, date); } - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { symbol = 0xFFFF; after = false; @@ -2948,7 +3049,7 @@ private: MsgId _minId, _maxId; int32 _count; - TextLinkPtr _lnk; + ClickHandlerPtr _lnk; void updateText(); @@ -2962,7 +3063,7 @@ public: } void draw(Painter &p, const QRect &r, uint32 selection, uint64 ms) const; - void getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; + void getState(ClickHandlerPtr &lnk, HistoryCursorState &state, int32 x, int32 y) const; void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { symbol = 0xFFFF; after = false; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index f1f2764be..ce29895ef 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -42,7 +42,6 @@ HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, His , _peer(history->peer) , _migrated(history->peer->migrateFrom() ? App::history(history->peer->migrateFrom()->id) : nullptr) , _history(history) -, _botInfo(history->peer->isUser() ? history->peer->asUser()->botInfo : nullptr) , _widget(historyWidget) , _scroll(scroll) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); @@ -55,9 +54,7 @@ HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, His _trippleClickTimer.setSingleShot(true); - if (_botInfo && !_botInfo->inited && App::api()) { - App::api()->requestFullPeer(_peer); - } + notifyIsBotChanged(); setMouseTracking(true); } @@ -205,16 +202,16 @@ void HistoryInner::paintEvent(QPaintEvent *e) { } uint64 ms = getms(); - if (!_firstLoading && _botInfo && !_botInfo->text.isEmpty() && _botDescHeight > 0) { - if (r.y() < _botDescRect.y() + _botDescRect.height() && r.y() + r.height() > _botDescRect.y()) { + if (!_firstLoading && _botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) { + if (r.y() < _botAbout->rect.y() + _botAbout->rect.height() && r.y() + r.height() > _botAbout->rect.y()) { textstyleSet(&st::inTextStyle); - App::roundRect(p, _botDescRect, st::msgInBg, MessageInCorners, &st::msgInShadow); + App::roundRect(p, _botAbout->rect, st::msgInBg, MessageInCorners, &st::msgInShadow); - p.setFont(st::msgNameFont->f); - p.setPen(st::black->p); - p.drawText(_botDescRect.left() + st::msgPadding.left(), _botDescRect.top() + st::msgPadding.top() + st::msgNameFont->ascent, lang(lng_bot_description)); + p.setFont(st::msgNameFont); + p.setPen(st::black); + p.drawText(_botAbout->rect.left() + st::msgPadding.left(), _botAbout->rect.top() + st::msgPadding.top() + st::msgNameFont->ascent, lang(lng_bot_description)); - _botInfo->text.draw(p, _botDescRect.left() + st::msgPadding.left(), _botDescRect.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip, _botDescWidth); + _botAbout->info->text.draw(p, _botAbout->rect.left() + st::msgPadding.left(), _botAbout->rect.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip, _botAbout->width); textstyleRestore(); } @@ -519,7 +516,7 @@ void HistoryInner::touchEvent(QTouchEvent *e) { } void HistoryInner::mouseMoveEvent(QMouseEvent *e) { - if (!(e->buttons() & (Qt::LeftButton | Qt::MiddleButton)) && (textlnkDown() || _dragAction != NoDrag)) { + if (!(e->buttons() & (Qt::LeftButton | Qt::MiddleButton)) && _dragAction != NoDrag) { mouseReleaseEvent(e); } dragActionUpdate(e->globalPos()); @@ -556,25 +553,20 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt dragActionUpdate(screenPos); if (button != Qt::LeftButton) return; + ClickHandler::pressed(); if (App::pressedItem() != App::hoveredItem()) { repaintItem(App::pressedItem()); App::pressedItem(App::hoveredItem()); repaintItem(App::pressedItem()); } - if (textlnkDown() != textlnkOver()) { - repaintItem(App::pressedLinkItem()); - textlnkDown(textlnkOver()); - App::pressedLinkItem(App::hoveredLinkItem()); - repaintItem(App::pressedLinkItem()); - repaintItem(App::pressedItem()); - } _dragAction = NoDrag; _dragItem = App::mousedItem(); _dragStartPos = mapMouseToItem(mapFromGlobal(screenPos), _dragItem); _dragWasInactive = App::wnd()->inactivePress(); if (_dragWasInactive) App::wnd()->inactivePress(false); - if (textlnkDown()) { + + if (ClickHandler::getPressed()) { _dragAction = PrepareDrag; } else if (!_selected.isEmpty()) { if (_selected.cbegin().value() == FullSelection) { @@ -694,15 +686,16 @@ void HistoryInner::onDragExec() { } } } + ClickHandlerPtr pressedHandler = ClickHandler::getPressed(); QString sel; QList<QUrl> urls; if (uponSelected) { sel = getSelectedText(); - } else if (textlnkDown()) { - sel = textlnkDown()->encoded(); - if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') { -// urls.push_back(QUrl::fromEncoded(sel.toUtf8())); // Google Chrome crashes in Mac OS X O_o - } + } else if (pressedHandler) { + sel = pressedHandler->dragText(); + //if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') { + // urls.push_back(QUrl::fromEncoded(sel.toUtf8())); // Google Chrome crashes in Mac OS X O_o + //} } if (!sel.isEmpty()) { updateDragSelection(0, 0, false); @@ -721,26 +714,28 @@ void HistoryInner::onDragExec() { if (App::main()) App::main()->updateAfterDrag(); return; } else { - HistoryItem *pressedLnkItem = App::pressedLinkItem(), *pressedItem = App::pressedItem(); - QLatin1String lnkType = (textlnkDown() && pressedLnkItem) ? textlnkDown()->type() : qstr(""); - bool lnkPhoto = (lnkType == qstr("PhotoLink")), - lnkVideo = (lnkType == qstr("VideoOpenLink")), - lnkAudio = (lnkType == qstr("AudioOpenLink")), - lnkDocument = (lnkType == qstr("DocumentOpenLink") || lnkType == qstr("GifOpenLink")), - lnkContact = (lnkType == qstr("PeerLink") && dynamic_cast<HistoryContact*>(pressedLnkItem->getMedia())), - dragSticker = dynamic_cast<HistorySticker*>(pressedItem ? pressedItem->getMedia() : 0), - dragByDate = (_dragCursorState == HistoryInDateCursorState); - if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument || lnkContact || dragSticker || dragByDate) { + QString forwardMimeType; + HistoryMedia *pressedMedia = nullptr; + if (HistoryItem *pressedItem = App::pressedItem()) { + pressedMedia = pressedItem->getMedia(); + if (_dragCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { + forwardMimeType = qsl("application/x-td-forward-pressed"); + } + } + if (HistoryItem *pressedLnkItem = App::pressedLinkItem()) { + if ((pressedMedia = pressedLnkItem->getMedia())) { + if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { + forwardMimeType = qsl("application/x-td-forward-pressed-link"); + } + } + } + if (!forwardMimeType.isEmpty()) { QDrag *drag = new QDrag(App::wnd()); QMimeData *mimeData = new QMimeData; - if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument || lnkContact) { - mimeData->setData(qsl("application/x-td-forward-pressed-link"), "1"); - } else { - mimeData->setData(qsl("application/x-td-forward-pressed"), "1"); - } - if (lnkDocument) { - QString filepath = static_cast<DocumentOpenLink*>(textlnkDown().data())->document()->filepath(DocumentData::FilePathResolveChecked); + mimeData->setData(forwardMimeType, "1"); + if (DocumentData *document = (pressedMedia ? pressedMedia->getDocument() : nullptr)) { + QString filepath = document->filepath(DocumentData::FilePathResolveChecked); if (!filepath.isEmpty()) { QList<QUrl> urls; urls.push_back(QUrl::fromLocalFile(filepath)); @@ -776,47 +771,32 @@ void HistoryInner::itemRemoved(HistoryItem *item) { } void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton button) { - TextLinkPtr needClick; - dragActionUpdate(screenPos); - if (textlnkOver()) { - if (textlnkDown() == textlnkOver() && _dragAction != Dragging) { - needClick = textlnkDown(); - - QLatin1String lnkType = needClick->type(); - bool lnkPhoto = (lnkType == qstr("PhotoLink")), - lnkVideo = (lnkType == qstr("VideoOpenLink")), - lnkAudio = (lnkType == qstr("AudioOpenLink")), - lnkDocument = (lnkType == qstr("DocumentOpenLink") || lnkType == qstr("GifOpenLink")), - lnkContact = (lnkType == qstr("PeerLink") && dynamic_cast<HistoryContact*>(App::pressedLinkItem() ? App::pressedLinkItem()->getMedia() : 0)); - if (_dragAction == PrepareDrag && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && button != Qt::RightButton) { - if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument || lnkContact) { - needClick = TextLinkPtr(); + ClickHandlerPtr activated = ClickHandler::unpressed(); + if (_dragAction == Dragging) { + activated.clear(); + } else if (HistoryItem *pressed = App::pressedLinkItem()) { + // if we are in selecting items mode perhaps we want to + // toggle selection instead of activating the pressed link + if (_dragAction == PrepareDrag && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && button != Qt::RightButton) { + if (HistoryMedia *media = pressed->getMedia()) { + if (media->toggleSelectionByHandlerClick(activated)) { + activated.clear(); } } } } - if (textlnkDown()) { - repaintItem(App::pressedLinkItem()); - textlnkDown(TextLinkPtr()); - App::pressedLinkItem(0); - if (!textlnkOver() && _cursor != style::cur_default) { - _cursor = style::cur_default; - setCursor(_cursor); - } - } if (App::pressedItem()) { repaintItem(App::pressedItem()); - App::pressedItem(0); + App::pressedItem(nullptr); } _wasSelectedText = false; - if (needClick) { - DEBUG_LOG(("Will click link: %1 (%2) %3").arg(needClick->text()).arg(needClick->readable()).arg(needClick->encoded())); + if (activated) { dragActionCancel(); - App::activateTextLink(needClick, button); + App::activateClickHandler(activated, button); return; } if (_dragAction == PrepareSelect && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { @@ -943,10 +923,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu = new PopupMenu(); - _contextMenuLnk = textlnkOver(); + _contextMenuLnk = ClickHandler::getActive(); HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem(); - PhotoLink *lnkPhoto = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); - DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data()); + PhotoClickHandler *lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLnk.data()); + DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data()); bool lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideo() : false; bool lnkIsAudio = lnkDocument ? (lnkDocument->document()->voice() != nullptr) : false; bool lnkIsSong = lnkDocument ? (lnkDocument->document()->song() != nullptr) : false; @@ -1073,16 +1053,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } - QLatin1String linktype = _contextMenuLnk ? _contextMenuLnk->type() : qstr(""); - if (linktype == qstr("TextLink") || linktype == qstr("LocationLink")) { - _menu->addAction(lang(lng_context_copy_link), this, SLOT(copyContextUrl()))->setEnabled(true); - } else if (linktype == qstr("EmailLink")) { - _menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true); - } else if (linktype == qstr("MentionLink")) { - _menu->addAction(lang(lng_context_copy_mention), this, SLOT(copyContextUrl()))->setEnabled(true); - } else if (linktype == qstr("HashtagLink")) { - _menu->addAction(lang(lng_context_copy_hashtag), this, SLOT(copyContextUrl()))->setEnabled(true); - } else { + QString copyToClipboardContextItem = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItem() : QString(); + if (!copyToClipboardContextItem.isEmpty()) { + _menu->addAction(copyToClipboardContextItem, this, SLOT(copyContextUrl()))->setEnabled(true); } if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) { _menu->addAction(lang(lng_context_copy_post_link), _widget, SLOT(onCopyPostLink())); @@ -1139,14 +1112,13 @@ void HistoryInner::copySelectedText() { } void HistoryInner::copyContextUrl() { - QString enc = _contextMenuLnk->encoded(); - if (!enc.isEmpty()) { - QApplication::clipboard()->setText(enc); + if (_contextMenuLnk) { + _contextMenuLnk->copyToClipboard(); } } void HistoryInner::saveContextImage() { - PhotoLink *lnk = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); + PhotoClickHandler *lnk = dynamic_cast<PhotoClickHandler*>(_contextMenuLnk.data()); if (!lnk) return; PhotoData *photo = lnk->photo(); @@ -1161,7 +1133,7 @@ void HistoryInner::saveContextImage() { } void HistoryInner::copyContextImage() { - PhotoLink *lnk = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); + PhotoClickHandler *lnk = dynamic_cast<PhotoClickHandler*>(_contextMenuLnk.data()); if (!lnk) return; PhotoData *photo = lnk->photo(); @@ -1171,7 +1143,7 @@ void HistoryInner::copyContextImage() { } void HistoryInner::cancelContextDownload() { - if (DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data())) { + if (DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data())) { lnkDocument->document()->cancel(); } else if (HistoryItem *item = App::contextItem()) { if (HistoryMedia *media = item->getMedia()) { @@ -1184,7 +1156,7 @@ void HistoryInner::cancelContextDownload() { void HistoryInner::showContextInFolder() { QString filepath; - if (DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data())) { + if (DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data())) { filepath = lnkDocument->document()->filepath(DocumentData::FilePathResolveChecked); } else if (HistoryItem *item = App::contextItem()) { if (HistoryMedia *media = item->getMedia()) { @@ -1199,12 +1171,12 @@ void HistoryInner::showContextInFolder() { } void HistoryInner::saveContextFile() { - if (DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data())) { - DocumentSaveLink::doSave(lnkDocument->document(), true); + if (DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data())) { + DocumentSaveClickHandler::doSave(lnkDocument->document(), true); } else if (HistoryItem *item = App::contextItem()) { if (HistoryMedia *media = item->getMedia()) { if (DocumentData *doc = media->getDocument()) { - DocumentSaveLink::doSave(doc, true); + DocumentSaveClickHandler::doSave(doc, true); } } } @@ -1298,8 +1270,8 @@ void HistoryInner::recountHeight() { int ph = _scroll->height(), minadd = 0; int wasYSkip = ph - historyHeight() - st::historyPadding; - if (_botInfo && !_botInfo->text.isEmpty()) { - minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botDescHeight; + if (_botAbout && !_botAbout->info->text.isEmpty()) { + minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height; } if (wasYSkip < minadd) wasYSkip = minadd; @@ -1325,33 +1297,33 @@ void HistoryInner::recountHeight() { } updateBotInfo(false); - if (_botInfo && !_botInfo->text.isEmpty()) { + if (_botAbout && !_botAbout->info->text.isEmpty()) { int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right(); if (tw > st::msgMaxWidth) tw = st::msgMaxWidth; tw -= st::msgPadding.left() + st::msgPadding.right(); - int32 mw = qMax(_botInfo->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); + int32 mw = qMax(_botAbout->info->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); if (tw > mw) tw = mw; - _botDescWidth = tw; - _botDescHeight = _botInfo->text.countHeight(_botDescWidth); + _botAbout->width = tw; + _botAbout->height = _botAbout->info->text.countHeight(_botAbout->width); - int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); + int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom(); int32 descMaxWidth = _scroll->width(); if (Adaptive::Wide()) { descMaxWidth = qMin(descMaxWidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left())); } - int32 descAtX = (descMaxWidth - _botDescWidth) / 2 - st::msgPadding.left(); + int32 descAtX = (descMaxWidth - _botAbout->width) / 2 - st::msgPadding.left(); int32 descAtY = qMin(_historyOffset - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top(); - _botDescRect = QRect(descAtX, descAtY, _botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); - } else { - _botDescWidth = _botDescHeight = 0; - _botDescRect = QRect(); + _botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); + } else if (_botAbout) { + _botAbout->width = _botAbout->height = 0; + _botAbout->rect = QRect(); } int32 newYSkip = ph - historyHeight() - st::historyPadding; - if (_botInfo && !_botInfo->text.isEmpty()) { - minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botDescHeight; + if (_botAbout && !_botAbout->info->text.isEmpty()) { + minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height; } if (newYSkip < minadd) newYSkip = minadd; @@ -1365,38 +1337,38 @@ void HistoryInner::recountHeight() { } void HistoryInner::updateBotInfo(bool recount) { - int32 newh = 0; - if (_botInfo && !_botInfo->description.isEmpty()) { - if (_botInfo->text.isEmpty()) { - _botInfo->text.setText(st::msgFont, _botInfo->description, _historyBotNoMonoOptions); + int newh = 0; + if (_botAbout && !_botAbout->info->description.isEmpty()) { + if (_botAbout->info->text.isEmpty()) { + _botAbout->info->text.setText(st::msgFont, _botAbout->info->description, _historyBotNoMonoOptions); if (recount) { int32 tw = _scroll->width() - st::msgMargin.left() - st::msgMargin.right(); if (tw > st::msgMaxWidth) tw = st::msgMaxWidth; tw -= st::msgPadding.left() + st::msgPadding.right(); - int32 mw = qMax(_botInfo->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); + int32 mw = qMax(_botAbout->info->text.maxWidth(), st::msgNameFont->width(lang(lng_bot_description))); if (tw > mw) tw = mw; - _botDescWidth = tw; - newh = _botInfo->text.countHeight(_botDescWidth); + _botAbout->width = tw; + newh = _botAbout->info->text.countHeight(_botAbout->width); } } else if (recount) { - newh = _botDescHeight; + newh = _botAbout->height; } } - if (recount) { - if (_botDescHeight != newh) { - _botDescHeight = newh; + if (recount && _botAbout) { + if (_botAbout->height != newh) { + _botAbout->height = newh; updateSize(); } - if (_botDescHeight > 0) { - int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); - int32 descAtX = (_scroll->width() - _botDescWidth) / 2 - st::msgPadding.left(); + if (_botAbout->height > 0) { + int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom(); + int32 descAtX = (_scroll->width() - _botAbout->width) / 2 - st::msgPadding.left(); int32 descAtY = qMin(_historyOffset - descH, (_scroll->height() - descH) / 2) + st::msgMargin.top(); - _botDescRect = QRect(descAtX, descAtY, _botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); + _botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); } else { - _botDescWidth = 0; - _botDescRect = QRect(); + _botAbout->width = 0; + _botAbout->rect = QRect(); } } } @@ -1480,21 +1452,21 @@ void HistoryInner::visibleAreaUpdated(int top, int bottom) { void HistoryInner::updateSize() { int32 ph = _scroll->height(), minadd = 0; int32 newYSkip = ph - historyHeight() - st::historyPadding; - if (_botInfo && !_botInfo->text.isEmpty()) { - minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botDescHeight; + if (_botAbout && !_botAbout->info->text.isEmpty()) { + minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + _botAbout->height; } if (newYSkip < minadd) newYSkip = minadd; - if (_botDescHeight > 0) { - int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom(); + if (_botAbout && _botAbout->height > 0) { + int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + _botAbout->height + st::msgPadding.bottom() + st::msgMargin.bottom(); int32 descMaxWidth = _scroll->width(); if (Adaptive::Wide()) { descMaxWidth = qMin(descMaxWidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left())); } - int32 descAtX = (descMaxWidth - _botDescWidth) / 2 - st::msgPadding.left(); + int32 descAtX = (descMaxWidth - _botAbout->width) / 2 - st::msgPadding.left(); int32 descAtY = qMin(newYSkip - descH, qMax(0, (_scroll->height() - descH) / 2)) + st::msgMargin.top(); - _botDescRect = QRect(descAtX, descAtY, _botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); + _botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom()); } int32 yAdded = newYSkip - _historyOffset; @@ -1517,19 +1489,12 @@ void HistoryInner::enterEvent(QEvent *e) { void HistoryInner::leaveEvent(QEvent *e) { if (HistoryItem *item = App::hoveredItem()) { repaintItem(item); - App::hoveredItem(0); + App::hoveredItem(nullptr); } - if (textlnkOver()) { - if (HistoryItem *item = App::hoveredLinkItem()) { - item->linkOut(textlnkOver()); - repaintItem(item); - App::hoveredLinkItem(0); - } - textlnkOver(TextLinkPtr()); - if (!textlnkDown() && _cursor != style::cur_default) { - _cursor = style::cur_default; - setCursor(_cursor); - } + ClickHandler::clearActive(); + if (!ClickHandler::getPressed() && _cursor != style::cur_default) { + _cursor = style::cur_default; + setCursor(_cursor); } return QWidget::leaveEvent(e); } @@ -1708,24 +1673,22 @@ void HistoryInner::onUpdateSelected() { dragActionCancel(); } - Qt::CursorShape cur = style::cur_default; + ClickHandlerPtr lnk; + ClickHandlerHost *lnkhost = nullptr; HistoryCursorState cursorState = HistoryDefaultCursorState; - bool lnkChanged = false, lnkInDesc = false; - - TextLinkPtr lnk; if (point.y() < _historyOffset) { - if (_botInfo && !_botInfo->text.isEmpty() && _botDescHeight > 0) { + if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) { bool inText = false; - _botInfo->text.getState(lnk, inText, point.x() - _botDescRect.left() - st::msgPadding.left(), point.y() - _botDescRect.top() - st::msgPadding.top() - st::botDescSkip - st::msgNameFont->height, _botDescWidth); + _botAbout->info->text.getState(lnk, inText, point.x() - _botAbout->rect.left() - st::msgPadding.left(), point.y() - _botAbout->rect.top() - st::msgPadding.top() - st::botDescSkip - st::msgNameFont->height, _botAbout->width); + lnkhost = _botAbout.data(); cursorState = inText ? HistoryInTextCursorState : HistoryDefaultCursorState; - lnkInDesc = true; } } else if (item) { item->getState(lnk, cursorState, m.x(), m.y()); if (!lnk && m.x() >= st::msgMargin.left() && m.x() < st::msgMargin.left() + st::msgPhotoSize) { if (HistoryMessage *msg = item->toHistoryMessage()) { if (msg->hasFromPhoto()) { - enumerateUserpics([&lnk, msg, &point](HistoryMessage *message, int userpicTop) -> bool { + enumerateUserpics([&lnk, &lnkhost, msg, &point](HistoryMessage *message, int userpicTop) -> bool { // stop enumeration if the userpic is above our point if (userpicTop + st::msgPhotoSize <= point.y()) { return false; @@ -1733,7 +1696,8 @@ void HistoryInner::onUpdateSelected() { // stop enumeration if we've found a userpic under the cursor if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) { - lnk = message->from()->lnk; + lnk = message->from()->openLink(); + lnkhost = msg; return false; } return true; @@ -1742,35 +1706,15 @@ void HistoryInner::onUpdateSelected() { } } } - if (lnk != textlnkOver()) { - lnkChanged = true; - if (textlnkOver()) { - if (HistoryItem *item = App::hoveredLinkItem()) { - item->linkOut(textlnkOver()); - repaintItem(item); - } else { - update(_botDescRect); - } - } - textlnkOver(lnk); - PopupTooltip::Hide(); - App::hoveredLinkItem((lnk && !lnkInDesc) ? item : nullptr); - if (textlnkOver()) { - if (HistoryItem *item = App::hoveredLinkItem()) { - item->linkOver(textlnkOver()); - repaintItem(item); - } else { - update(_botDescRect); - } - } - } - if (cursorState != _dragCursorState) { + bool lnkChanged = ClickHandler::setActive(lnk, lnkhost); + if (lnkChanged || cursorState != _dragCursorState) { PopupTooltip::Hide(); } if (lnk || cursorState == HistoryInDateCursorState || cursorState == HistoryInForwardedCursorState) { PopupTooltip::Show(1000, this); } + Qt::CursorShape cur = style::cur_default; if (_dragAction == NoDrag) { _dragCursorState = cursorState; if (lnk) { @@ -1789,7 +1733,6 @@ void HistoryInner::onUpdateSelected() { _dragAction = Selecting; } } - cur = textlnkDown() ? style::cur_pointer : style::cur_default; if (_dragAction == Selecting) { bool canSelectMany = (_history != 0); if (item == _dragItem && item == App::hoveredItem() && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { @@ -1846,7 +1789,7 @@ void HistoryInner::onUpdateSelected() { } else if (_dragAction == Dragging) { } - if (textlnkDown()) { + if (ClickHandler::getPressed()) { cur = style::cur_pointer; } else if (_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { if (!_dragSelFrom || !_dragSelTo) { @@ -1886,6 +1829,14 @@ void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dr update(); } +void HistoryInner::BotAbout::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + _parent->update(rect); +} + +void HistoryInner::BotAbout::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + _parent->update(rect); +} + int HistoryInner::historyHeight() const { int result = 0; if (!_history || _history->isEmpty()) { @@ -1932,9 +1883,18 @@ int HistoryInner::itemTop(const HistoryItem *item) const { // -1 if should not b } void HistoryInner::notifyIsBotChanged() { - _botInfo = (_history && _history->peer->isUser()) ? _history->peer->asUser()->botInfo : 0; - if (_botInfo && !_botInfo->inited && App::api()) { - App::api()->requestFullPeer(_peer); + BotInfo *newinfo = (_history && _history->peer->isUser()) ? _history->peer->asUser()->botInfo : nullptr; + if ((!newinfo && !_botAbout) || (newinfo && _botAbout && _botAbout->info == newinfo)) { + return; + } + + if (newinfo) { + _botAbout.reset(new BotAbout(this, newinfo)); + if (newinfo && !newinfo->inited && App::api()) { + App::api()->requestFullPeer(_peer); + } + } else { + _botAbout.clear(); } } @@ -2019,10 +1979,7 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const { } QString HistoryInner::tooltipText() const { - TextLinkPtr lnk = textlnkOver(); - if (lnk && !lnk->fullDisplayed()) { - return lnk->readable(); - } else if (_dragCursorState == HistoryInDateCursorState && _dragAction == NoDrag) { + if (_dragCursorState == HistoryInDateCursorState && _dragAction == NoDrag) { if (App::hoveredItem()) { return App::hoveredItem()->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)); } @@ -2032,6 +1989,8 @@ QString HistoryInner::tooltipText() const { return fwd->_text.original(0, 0xFFFF, Text::ExpandLinksNone); } } + } else if (ClickHandlerPtr lnk = ClickHandler::getActive()) { + return lnk->tooltip(); } return QString(); } @@ -2210,6 +2169,8 @@ void BotKeyboard::Style::paintButtonBg(Painter &p, const QRect &rect, bool down, } void BotKeyboard::resizeEvent(QResizeEvent *e) { + if (!_impl) return; + updateStyle(); _height = _impl->naturalHeight() + 2 * _st->margin; @@ -2225,14 +2186,8 @@ void BotKeyboard::resizeEvent(QResizeEvent *e) { void BotKeyboard::mousePressEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); - if (textlnkDown() != textlnkOver()) { - Ui::repaintHistoryItem(App::pressedLinkItem()); - textlnkDown(textlnkOver()); - App::hoveredLinkItem(nullptr); - App::pressedLinkItem(App::hoveredLinkItem()); - Ui::repaintHistoryItem(App::pressedLinkItem()); - } - update(); + + ClickHandler::pressed(); } void BotKeyboard::mouseMoveEvent(QMouseEvent *e) { @@ -2241,13 +2196,11 @@ void BotKeyboard::mouseMoveEvent(QMouseEvent *e) { } void BotKeyboard::mouseReleaseEvent(QMouseEvent *e) { - TextLinkPtr down(textlnkDown()); - textlnkDown(TextLinkPtr()); - _lastMousePos = e->globalPos(); updateSelected(); - if (down && textlnkOver() == down) { - down->onClick(e->button()); + + if (ClickHandlerPtr activated = ClickHandler::unpressed()) { + App::activateClickHandler(activated, e->button()); } } @@ -2256,6 +2209,12 @@ void BotKeyboard::leaveEvent(QEvent *e) { updateSelected(); } +void BotKeyboard::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { +} + +void BotKeyboard::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { +} + bool BotKeyboard::updateMarkup(HistoryItem *to) { if (to && to->definesReplyKeyboard()) { if (_wasForMsgId == FullMsgId(to->channelId(), to->id)) return false; @@ -2284,7 +2243,7 @@ bool BotKeyboard::updateMarkup(HistoryItem *to) { _maximizeSize = _singleUse = _forceReply = false; _wasForMsgId = FullMsgId(); clearSelection(); - _impl.reset(); + _impl.clear(); return true; } return false; @@ -2335,9 +2294,8 @@ QPoint BotKeyboard::tooltipPos() const { } QString BotKeyboard::tooltipText() const { - TextLinkPtr lnk = textlnkOver(); - if (lnk && !lnk->fullDisplayed()) { - return lnk->readable(); + if (ClickHandlerPtr lnk = ClickHandler::getActive()) { + return lnk->tooltip(); } return QString(); } @@ -2345,29 +2303,16 @@ QString BotKeyboard::tooltipText() const { void BotKeyboard::updateSelected() { PopupTooltip::Show(1000, this); - if (textlnkDown() || !_impl) return; + if (!_impl) return; QPoint p(mapFromGlobal(_lastMousePos)); int x = rtl() ? st::botKbScroll.width : _st->margin; - TextLinkPtr lnk; + ClickHandlerPtr lnk; _impl->getState(lnk, p.x() - x, p.y() - _st->margin); - if (lnk != textlnkOver()) { - if (textlnkOver()) { - if (HistoryItem *item = App::hoveredLinkItem()) { - item->linkOut(textlnkOver()); - Ui::repaintHistoryItem(item); - } else { - App::main()->update();// update(_botDescRect); - _impl->linkOut(textlnkOver()); - } - } - textlnkOver(lnk); - _impl->linkOver(lnk); + if (ClickHandler::setActive(lnk, this)) { PopupTooltip::Hide(); - App::hoveredLinkItem(nullptr); setCursor(lnk ? style::cur_pointer : style::cur_default); - update(); } } @@ -2865,7 +2810,7 @@ void HistoryWidget::onStickersUpdated() { void HistoryWidget::onMentionHashtagOrBotCommandInsert(QString str) { if (str.at(0) == '/') { // bot command - App::sendBotCommand(str); + App::sendBotCommand(_peer, str); setFieldText(_field.getLastText().mid(_field.textCursor().position())); } else { _field.onMentionHashtagOrBotCommandInsert(str); @@ -4729,7 +4674,7 @@ void HistoryWidget::onBotStart() { QString token = _peer->asUser()->botInfo->startToken; if (token.isEmpty()) { - sendBotCommand(qsl("/start"), 0); + sendBotCommand(_peer, qsl("/start"), 0); } else { uint64 randomId = rand_value<uint64>(); MTP::send(MTPmessages_StartBot(_peer->asUser()->inputUser, MTP_inputPeerEmpty(), MTP_long(randomId), MTP_string(token)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _peer->asUser())); @@ -5158,8 +5103,8 @@ void HistoryWidget::stopRecording(bool send) { _a_record.start(); } -void HistoryWidget::sendBotCommand(const QString &cmd, MsgId replyTo) { // replyTo != 0 from ReplyKeyboardMarkup, == 0 from cmd links - if (!_history) return; +void HistoryWidget::sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo) { // replyTo != 0 from ReplyKeyboardMarkup, == 0 from cmd links + if (!_peer || _peer != peer) return; bool lastKeyboardUsed = (_keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard.forMsgId() == FullMsgId(_channel, replyTo)); @@ -5184,6 +5129,32 @@ void HistoryWidget::sendBotCommand(const QString &cmd, MsgId replyTo) { // reply _field.setFocus(); } +void HistoryWidget::sendBotCallback(PeerData *peer, const QString &cmd, MsgId replyTo) { + if (!_peer || _peer != peer) return; + + bool lastKeyboardUsed = (_keyboard.forMsgId() == FullMsgId(_channel, _history->lastKeyboardId)) && (_keyboard.forMsgId() == FullMsgId(_channel, replyTo)); + + MTP::send(MTPmessages_GetBotCallbackAnswer(_peer->input, MTP_int(replyTo), MTP_string(cmd)), rpcDone(&HistoryWidget::botCallbackDone), rpcFail(&HistoryWidget::botCallbackFail)); + + if (replyTo) { + cancelReply(); + if (_keyboard.singleUse() && _keyboard.hasMarkup() && lastKeyboardUsed) { + if (_kbShown) onKbToggle(false); + _history->lastKeyboardUsed = true; + } + } +} + +void HistoryWidget::botCallbackDone(const MTPmessages_BotCallbackAnswer &answer) { + +} + +bool HistoryWidget::botCallbackFail(const RPCError &error) { + if (mtpIsFlood(error)) return false; + + return true; +} + bool HistoryWidget::insertBotCommand(const QString &cmd, bool specialGif) { if (!_history) return false; @@ -6169,6 +6140,10 @@ bool HistoryWidget::ui_isInlineItemBeingChosen() { return _emojiPan.ui_isInlineItemBeingChosen(); } +PeerData *HistoryWidget::ui_getPeerForMouseAction() { + return _peer; +} + void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { if (_peer && _list && (item == App::mousedItem() || item == App::hoveredItem() || item == App::hoveredLinkItem())) { _list->onUpdateSelected(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 94e168cb4..626320a46 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -101,9 +101,9 @@ public: void notifyIsBotChanged(); void notifyMigrateUpdated(); - // AbstractTooltipShower - virtual QString tooltipText() const; - virtual QPoint tooltipPos() const; + // AbstractTooltipShower interface + QString tooltipText() const override; + QPoint tooltipPos() const override; ~HistoryInner(); @@ -149,10 +149,24 @@ private: // or at least we don't need to display first _history date (just skip it by height) int _historySkipHeight = 0; - BotInfo *_botInfo = nullptr; - int _botDescWidth = 0; - int _botDescHeight = 0; - QRect _botDescRect; + class BotAbout : public ClickHandlerHost { + public: + BotAbout(HistoryInner *parent, BotInfo *info) : _parent(parent), info(info) { + } + BotInfo *info = nullptr; + int width = 0; + int height = 0; + QRect rect; + + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; + + private: + HistoryInner *_parent; + + }; + UniquePointer<BotAbout> _botAbout; HistoryWidget *_widget = nullptr; ScrollArea *_scroll = nullptr; @@ -187,7 +201,7 @@ private: QPoint _trippleClickPoint; QTimer _trippleClickTimer; - TextLinkPtr _contextMenuLnk; + ClickHandlerPtr _contextMenuLnk; HistoryItem *_dragSelFrom = nullptr; HistoryItem *_dragSelTo = nullptr; @@ -285,7 +299,7 @@ private: }; -class BotKeyboard : public TWidget, public AbstractTooltipShower { +class BotKeyboard : public TWidget, public AbstractTooltipShower, public ClickHandlerHost { Q_OBJECT public: @@ -313,9 +327,13 @@ public: return _wasForMsgId; } - // AbstractTooltipShower - virtual QString tooltipText() const; - virtual QPoint tooltipPos() const; + // AbstractTooltipShower interface + QString tooltipText() const override; + QPoint tooltipPos() const override; + + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active); + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed); public slots: @@ -439,9 +457,9 @@ public: void mouseReleaseEvent(QMouseEvent *e); void leaveEvent(QEvent *e); - // AbstractTooltipShower - virtual QString tooltipText() const; - virtual QPoint tooltipPos() const; + // AbstractTooltipShower interface + QString tooltipText() const override; + QPoint tooltipPos() const override; }; @@ -582,7 +600,8 @@ public: void onListEscapePressed(); - void sendBotCommand(const QString &cmd, MsgId replyTo); + void sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo); + void sendBotCallback(PeerData *peer, const QString &cmd, MsgId replyTo); bool insertBotCommand(const QString &cmd, bool specialGif); bool eventFilter(QObject *obj, QEvent *e) override; @@ -629,6 +648,7 @@ public: void ui_repaintInlineItem(const LayoutInlineItem *gif); bool ui_isInlineItemVisible(const LayoutInlineItem *layout); bool ui_isInlineItemBeingChosen(); + PeerData *ui_getPeerForMouseAction(); void notify_historyItemLayoutChanged(const HistoryItem *item); void notify_botCommandsChanged(UserData *user); @@ -836,6 +856,9 @@ private: void addMessagesToFront(PeerData *peer, const QVector<MTPMessage> &messages, const QVector<MTPMessageGroup> *collapsed); void addMessagesToBack(PeerData *peer, const QVector<MTPMessage> &messages, const QVector<MTPMessageGroup> *collapsed); + void botCallbackDone(const MTPmessages_BotCallbackAnswer &answer); + bool botCallbackFail(const RPCError &error); + enum ScrollChangeType { ScrollChangeNone, ScrollChangeAdd, diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index f915ab3a7..bc4d8f691 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -28,20 +28,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "intro/introcode.h" namespace { - class SignUpLink : public ITextLink { - TEXT_LINK_CLASS(SignUpLink) - + class SignUpClickHandler : public LeftButtonClickHandler { public: - - SignUpLink(IntroPhone *widget) : _widget(widget) { + SignUpClickHandler(IntroPhone *widget) : _widget(widget) { } - void onClick(Qt::MouseButton) const { + protected: + void onClickImpl() const override { _widget->toSignUp(); } private: IntroPhone *_widget; + }; } @@ -71,7 +70,7 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent) connect(intro(), SIGNAL(countryChanged()), this, SLOT(countryChanged())); connect(&checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); - _signup.setLink(1, TextLinkPtr(new SignUpLink(this))); + _signup.setLink(1, MakeShared<SignUpClickHandler>(this)); _signup.hide(); _signupCache = myGrab(&_signup); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index 794d81de5..b1f3d572d 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -217,24 +217,32 @@ RoundCorners documentCorners(int32 colorIndex) { return RoundCorners(DocBlueCorners + (colorIndex & 3)); } -void LayoutRadialProgressItem::linkOver(const TextLinkPtr &lnk) { - if (lnk == _openl || lnk == _savel || lnk == _cancell) { - a_iconOver.start(1); - _a_iconOver.start(); - } +void LayoutMediaItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + App::hoveredLinkItem(active ? _parent : nullptr); + Ui::repaintHistoryItem(_parent); } -void LayoutRadialProgressItem::linkOut(const TextLinkPtr &lnk) { - if (lnk == _openl || lnk == _savel || lnk == _cancell) { - a_iconOver.start(0); - _a_iconOver.start(); - } +void LayoutMediaItem::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + App::pressedLinkItem(pressed ? _parent : nullptr); + Ui::repaintHistoryItem(_parent); } -void LayoutRadialProgressItem::setLinks(ITextLink *openl, ITextLink *savel, ITextLink *cancell) { - _openl.reset(openl); - _savel.reset(savel); - _cancell.reset(cancell); +void LayoutRadialProgressItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + if (p == _openl || p == _savel || p == _cancell) { + a_iconOver.start(active ? 1 : 0); + _a_iconOver.start(); + } + LayoutMediaItem::clickHandlerActiveChanged(p, active); +} + +void LayoutRadialProgressItem::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + LayoutMediaItem::clickHandlerPressedChanged(p, pressed); +} + +void LayoutRadialProgressItem::setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell) { + _openl = std_::move(openl); + _savel = std_::move(savel); + _cancell = std_::move(cancell); } void LayoutRadialProgressItem::step_iconOver(float64 ms, bool timer) { @@ -314,7 +322,7 @@ void LayoutOverviewDate::paint(Painter &p, const QRect &clip, uint32 selection, LayoutOverviewPhoto::LayoutOverviewPhoto(PhotoData *photo, HistoryItem *parent) : LayoutMediaItem(parent) , _data(photo) -, _link(new PhotoLink(photo)) +, _link(new PhotoOpenClickHandler(photo)) , _goodLoaded(false) { } @@ -380,7 +388,7 @@ void LayoutOverviewPhoto::paint(Painter &p, const QRect &clip, uint32 selection, } } -void LayoutOverviewPhoto::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutOverviewPhoto::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { if (hasPoint(x, y)) { link = _link; } @@ -390,7 +398,7 @@ LayoutOverviewVideo::LayoutOverviewVideo(DocumentData *video, HistoryItem *paren , _data(video) , _duration(formatDurationText(_data->duration())) , _thumbLoaded(false) { - setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data); } void LayoutOverviewVideo::initDimensions() { @@ -485,7 +493,7 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, p.setOpacity((st::msgDateImgBg->c.alphaF() * (1 - over)) + (st::msgDateImgBgOver->c.alphaF() * over)); p.setBrush(st::black); } else { - bool over = textlnkDrawOver(loaded ? _openl : (_data->loading() ? _cancell : _savel)); + bool over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _savel)); p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); } @@ -517,7 +525,7 @@ void LayoutOverviewVideo::paint(Painter &p, const QRect &clip, uint32 selection, } } -void LayoutOverviewVideo::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutOverviewVideo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { bool loaded = _data->loaded(); if (hasPoint(x, y)) { @@ -552,17 +560,18 @@ void LayoutOverviewVideo::updateStatusText() const { LayoutOverviewVoice::LayoutOverviewVoice(DocumentData *voice, HistoryItem *parent) : LayoutAbstractFileItem(parent) , _data(voice) -, _namel(new DocumentOpenLink(_data)) { +, _namel(new DocumentOpenClickHandler(_data)) { AddComponents(OverviewItemInfo::Bit()); t_assert(_data->voice() != 0); - setLinks(new DocumentOpenLink(_data), new DocumentOpenLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data); + updateName(); QString d = textcmdLink(1, textRichPrepare(langDateTime(date(_data->date)))); TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto }; _details.setText(st::normalFont, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts); - _details.setLink(1, TextLinkPtr(new MessageLink(parent))); + _details.setLink(1, MakeShared<GoToMessageClickHandler>(parent)); } void LayoutOverviewVoice::initDimensions() { @@ -610,7 +619,7 @@ void LayoutOverviewVoice::paint(Painter &p, const QRect &clip, uint32 selection, float64 over = a_iconOver.current(); p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); } else { - bool over = textlnkDrawOver(loaded ? _openl : (_data->loading() ? _cancell : _openl)); + bool over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); } @@ -669,7 +678,7 @@ void LayoutOverviewVoice::paint(Painter &p, const QRect &clip, uint32 selection, } } -void LayoutOverviewVoice::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutOverviewVoice::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { bool loaded = _data->loaded(); bool showPause = updateStatusText(); @@ -746,8 +755,8 @@ bool LayoutOverviewVoice::updateStatusText() const { LayoutOverviewDocument::LayoutOverviewDocument(DocumentData *document, HistoryItem *parent) : LayoutAbstractFileItem(parent) , _data(document) -, _msgl(new MessageLink(parent)) -, _namel(new DocumentOpenLink(_data)) +, _msgl(new GoToMessageClickHandler(parent)) +, _namel(new DocumentOpenClickHandler(_data)) , _thumbForLoaded(false) , _name(documentName(_data)) , _date(langDateTime(date(_data->date))) @@ -756,7 +765,7 @@ LayoutOverviewDocument::LayoutOverviewDocument(DocumentData *document, HistoryIt , _colorIndex(documentColorIndex(_data, _ext)) { AddComponents(OverviewItemInfo::Bit()); - setLinks(new DocumentOpenLink(_data), new DocumentSaveLink(_data), new DocumentCancelLink(_data)); + setDocumentLinks(_data); setStatusSize(FileStatusSizeReady, _data->size, _data->song() ? _data->song()->duration : -1, 0); @@ -826,7 +835,7 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti float64 over = a_iconOver.current(); p.setBrush(style::interpolate(st::msgFileInBg, st::msgFileInBgOver, over)); } else { - bool over = textlnkDrawOver(loaded ? _openl : (_data->loading() ? _cancell : _openl)); + bool over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); p.setBrush(over ? st::msgFileInBgOver : st::msgFileInBg); } @@ -908,7 +917,7 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti p.setBrush(style::interpolate(documentDarkColor(_colorIndex), documentOverColor(_colorIndex), over)); } } else { - bool over = textlnkDrawOver(_data->loading() ? _cancell : _savel); + bool over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel); p.setBrush(over ? (wthumb ? st::msgDateImgBgOver : documentOverColor(_colorIndex)) : (wthumb ? st::msgDateImgBg : documentDarkColor(_colorIndex))); } p.setOpacity(radialOpacity * p.opacity()); @@ -959,13 +968,13 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti p.drawTextLeft(nameleft, statustop, _width, _statusText); } if (datetop >= 0 && clip.intersects(rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width))) { - p.setFont(textlnkDrawOver(_msgl) ? st::normalFont->underline() : st::normalFont); + p.setFont(ClickHandler::showAsActive(_msgl) ? st::normalFont->underline() : st::normalFont); p.setPen(st::mediaInFg); p.drawTextLeft(nameleft, datetop, _width, _date, _datew); } } -void LayoutOverviewDocument::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutOverviewDocument::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { bool loaded = _data->loaded() || Local::willStickerImageLoad(mediaKey(DocumentFileLocation, _data->dc, _data->id)); bool showPause = updateStatusText(); @@ -1062,14 +1071,14 @@ bool LayoutOverviewDocument::updateStatusText() const { } namespace { - ITextLink *linkFromUrl(const QString &url) { + TextClickHandlerPtr clickHandlerFromUrl(const QString &url) { int32 at = url.indexOf('@'), slash = url.indexOf('/'); if ((at > 0) && (slash < 0 || slash > at)) { - return new EmailLink(url); + return MakeShared<EmailClickHandler>(url); } - return new TextLink(url); + return MakeShared<UrlClickHandler>(url); } -} +} // namespace LayoutOverviewLink::LayoutOverviewLink(HistoryMedia *media, HistoryItem *parent) : LayoutMediaItem(parent) { AddComponents(OverviewItemInfo::Bit()); @@ -1109,20 +1118,20 @@ LayoutOverviewLink::LayoutOverviewLink(HistoryMedia *media, HistoryItem *parent) _page = (media && media->type() == MediaTypeWebPage) ? static_cast<HistoryWebPage*>(media)->webpage() : 0; if (_page) { if (_page->doc) { - _photol = TextLinkPtr(new DocumentOpenLink(_page->doc)); + _photol.reset(new DocumentOpenClickHandler(_page->doc)); } else if (_page->photo) { if (_page->type == WebPageProfile || _page->type == WebPageVideo) { - _photol = TextLinkPtr(linkFromUrl(_page->url)); + _photol = clickHandlerFromUrl(_page->url); } else if (_page->type == WebPagePhoto || _page->siteName == qstr("Twitter") || _page->siteName == qstr("Facebook")) { - _photol = TextLinkPtr(new PhotoLink(_page->photo)); + _photol.reset(new PhotoOpenClickHandler(_page->photo)); } else { - _photol = TextLinkPtr(linkFromUrl(_page->url)); + _photol = clickHandlerFromUrl(_page->url); } } else { - _photol = TextLinkPtr(linkFromUrl(_page->url)); + _photol = clickHandlerFromUrl(_page->url); } } else if (!_links.isEmpty()) { - _photol = TextLinkPtr(linkFromUrl(_links.at(0).lnk->text())); + _photol = clickHandlerFromUrl(_links.front().lnk->text()); } if (from >= till && _page) { text = _page->description; @@ -1277,7 +1286,7 @@ void LayoutOverviewLink::paint(Painter &p, const QRect &clip, uint32 selection, p.setPen(st::btnYesColor); for (int32 i = 0, l = _links.size(); i < l; ++i) { if (clip.intersects(rtlrect(left, top, qMin(w, _links.at(i).width), st::normalFont->height, _width))) { - p.setFont(textlnkDrawOver(_links.at(i).lnk) ? st::normalFont->underline() : st::normalFont); + p.setFont(ClickHandler::showAsActive(_links.at(i).lnk) ? st::normalFont->underline() : st::normalFont); p.drawTextLeft(left, top, _width, (w < _links.at(i).width) ? st::normalFont->elided(_links.at(i).text, w) : _links.at(i).text); } top += st::normalFont->height; @@ -1291,7 +1300,7 @@ void LayoutOverviewLink::paint(Painter &p, const QRect &clip, uint32 selection, } } -void LayoutOverviewLink::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutOverviewLink::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { int32 left = st::dlgPhotoSize + st::dlgPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left; if (rtlrect(0, top, st::dlgPhotoSize, st::dlgPhotoSize, _width).contains(x, y)) { link = _photol; @@ -1323,7 +1332,7 @@ void LayoutOverviewLink::getState(TextLinkPtr &link, HistoryCursorState &cursor, LayoutOverviewLink::Link::Link(const QString &url, const QString &text) : text(text) , width(st::normalFont->width(text)) -, lnk(linkFromUrl(url)) { +, lnk(clickHandlerFromUrl(url)) { } LayoutInlineItem::LayoutInlineItem(InlineResult *result, DocumentData *doc, PhotoData *photo) : LayoutItem() @@ -1378,8 +1387,8 @@ void LayoutInlineItem::update() { LayoutInlineGif::LayoutInlineGif(InlineResult *result, DocumentData *doc, bool saved) : LayoutInlineItem(result, doc, 0) , _state(0) , _gif(0) -, _send(new SendInlineItemLink()) -, _delete((doc && saved) ? new DeleteSavedGifLink(doc) : 0) +, _send(new SendInlineItemClickHandler()) +, _delete((doc && saved) ? new DeleteSavedGifClickHandler(doc) : nullptr) , _animation(0) { } @@ -1402,9 +1411,7 @@ void LayoutInlineGif::setPosition(int32 position) { } } -void DeleteSavedGifLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; - +void DeleteSavedGifClickHandler::onClickImpl() const { int32 index = cSavedGifs().indexOf(_data); if (index >= 0) { cRefSavedGifs().remove(index); @@ -1490,7 +1497,7 @@ void LayoutInlineGif::paint(Painter &p, const QRect &clip, uint32 selection, con } } -void LayoutInlineGif::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutInlineGif::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { if (x >= 0 && x < _width && y >= 0 && y < st::inlineMediaHeight) { if (_delete && (rtl() ? _width - x : x) >= _width - st::stickerPanDelete.pxWidth() && y < st::stickerPanDelete.pxHeight()) { link = _delete; @@ -1500,45 +1507,43 @@ void LayoutInlineGif::getState(TextLinkPtr &link, HistoryCursorState &cursor, in } } -void LayoutInlineGif::linkOver(const TextLinkPtr &link) { - if (_delete && link == _delete) { - if (!(_state & StateDeleteOver)) { - EnsureAnimation(_a_deleteOver, 0, func(this, &LayoutInlineGif::update)); - _state |= StateDeleteOver; - _a_deleteOver.start(1, st::stickersRowDuration); - } - } - if ((_delete && link == _delete) || link == _send) { - if (!content_loaded()) { - ensureAnimation(); - if (!(_state & StateOver)) { - EnsureAnimation(_animation->_a_over, 0, func(this, &LayoutInlineGif::update)); - _animation->_a_over.start(1, st::stickersRowDuration); +void LayoutInlineGif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + if (!p) return; + + if (_delete && p == _delete) { + bool wasactive = (_state & StateDeleteOver); + if (active != wasactive) { + float64 from = active ? 0 : 1, to = active ? 1 : 0; + EnsureAnimation(_a_deleteOver, from, func(this, &LayoutInlineGif::update)); + _a_deleteOver.start(to, st::stickersRowDuration); + if (active) { + _state |= StateDeleteOver; + } else { + _state &= ~StateDeleteOver; } } - _state |= StateOver; } + if (p == _delete || p == _send) { + bool wasactive = (_state & StateOver); + if (active != wasactive) { + if (!content_loaded()) { + ensureAnimation(); + float64 from = active ? 0 : 1, to = active ? 1 : 0; + EnsureAnimation(_animation->_a_over, from, func(this, &LayoutInlineGif::update)); + _animation->_a_over.start(to, st::stickersRowDuration); + } + if (active) { + _state |= StateOver; + } else { + _state &= ~StateOver; + } + } + } + LayoutInlineItem::clickHandlerActiveChanged(p, active); } -void LayoutInlineGif::linkOut(const TextLinkPtr &link) { - if (_delete && link == _delete) { - if (_state & StateDeleteOver) { - update(); - EnsureAnimation(_a_deleteOver, 1, func(this, &LayoutInlineItem::update)); - _state &= ~StateDeleteOver; - _a_deleteOver.start(0, st::stickersRowDuration); - } - } - if ((_delete && link == _delete) || link == _send) { - if (!content_loaded()) { - ensureAnimation(); - if (_state & StateOver) { - EnsureAnimation(_animation->_a_over, 1, func(this, &LayoutInlineItem::update)); - _animation->_a_over.start(0, st::stickersRowDuration); - } - } - _state &= ~StateOver; - } +void LayoutInlineGif::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + LayoutInlineItem::clickHandlerPressedChanged(p, pressed); } QSize LayoutInlineGif::countFrameSize() const { @@ -1728,7 +1733,7 @@ QByteArray LayoutInlineGif::content_data() const { } LayoutInlinePhoto::LayoutInlinePhoto(InlineResult *result, PhotoData *photo) : LayoutInlineItem(result, 0, photo) -, _send(new SendInlineItemLink()) +, _send(new SendInlineItemClickHandler()) , _thumbLoaded(false) { } @@ -1759,7 +1764,7 @@ void LayoutInlinePhoto::paint(Painter &p, const QRect &clip, uint32 selection, c } } -void LayoutInlinePhoto::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutInlinePhoto::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { if (x >= 0 && x < _width && y >= 0 && y < st::inlineMediaHeight) { link = _send; } @@ -1853,10 +1858,12 @@ void LayoutInlinePhoto::content_forget() { } LayoutInlineWebVideo::LayoutInlineWebVideo(InlineResult *result) : LayoutInlineItem(result, 0, 0) -, _send(new SendInlineItemLink()) -, _link(result->content_url.isEmpty() ? 0 : linkFromUrl(result->content_url)) +, _send(new SendInlineItemClickHandler()) , _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) , _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) { + if (!result->content_url.isEmpty()) { + _link = clickHandlerFromUrl(result->content_url); + } if (_result->duration) { _duration = formatDurationText(_result->duration); } @@ -1911,7 +1918,7 @@ void LayoutInlineWebVideo::paint(Painter &p, const QRect &clip, uint32 selection } } -void LayoutInlineWebVideo::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutInlineWebVideo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { if (x >= 0 && x < st::inlineThumbSize && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) { link = _link; return; @@ -1945,12 +1952,16 @@ void LayoutInlineWebVideo::prepareThumb(int32 width, int32 height) const { } LayoutInlineArticle::LayoutInlineArticle(InlineResult *result, bool withThumb) : LayoutInlineItem(result, 0, 0) -, _send(new SendInlineItemLink()) -, _url(result->url.isEmpty() ? 0 : linkFromUrl(result->url)) -, _link(result->content_url.isEmpty() ? 0 : linkFromUrl(result->content_url)) +, _send(new SendInlineItemClickHandler()) , _withThumb(withThumb) , _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) , _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) { + if (!result->url.isEmpty()) { + _url = clickHandlerFromUrl(result->url); + } + if (!result->content_url.isEmpty()) { + _link = clickHandlerFromUrl(result->content_url); + } QVector<QStringRef> parts = _result->url.splitRef('/'); if (!parts.isEmpty()) { QStringRef domain = parts.at(0); @@ -2046,7 +2057,7 @@ void LayoutInlineArticle::paint(Painter &p, const QRect &clip, uint32 selection, } } -void LayoutInlineArticle::getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { +void LayoutInlineArticle::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { int32 left = _withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0; if (x >= 0 && x < left - st::inlineThumbSkip && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) { link = _link; diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index 88d3cc4dd..31ddea8cb 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -92,16 +92,16 @@ public: bool selecting; virtual const OverviewPaintContext *toOverviewPaintContext() const { - return 0; + return nullptr; } virtual const InlinePaintContext *toInlinePaintContext() const { - return 0; + return nullptr; } }; class LayoutMediaItem; -class LayoutItem : public Composer { +class LayoutItem : public Composer, public ClickHandlerHost { public: LayoutItem() { } @@ -121,8 +121,8 @@ public: } virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const = 0; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { - link = TextLinkPtr(); + virtual void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const { + link.clear(); cursor = HistoryDefaultCursorState; } virtual void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { // from text @@ -130,10 +130,6 @@ public: symbol = upon ? 0xFFFF : 0; after = false; } - virtual void linkOver(const TextLinkPtr &lnk) { - } - virtual void linkOut(const TextLinkPtr &lnk) { - } int32 width() const { return _width; @@ -150,17 +146,17 @@ public: } virtual LayoutMediaItem *toLayoutMediaItem() { - return 0; + return nullptr; } virtual const LayoutMediaItem *toLayoutMediaItem() const { - return 0; + return nullptr; } virtual HistoryItem *getItem() const { - return 0; + return nullptr; } virtual DocumentData *getDocument() const { - return 0; + return nullptr; } MsgId msgId() const { const HistoryItem *item = getItem(); @@ -180,16 +176,19 @@ public: LayoutMediaItem(HistoryItem *parent) : _parent(parent) { } - virtual LayoutMediaItem *toLayoutMediaItem() { + LayoutMediaItem *toLayoutMediaItem() override { return this; } - virtual const LayoutMediaItem *toLayoutMediaItem() const { + const LayoutMediaItem *toLayoutMediaItem() const override { return this; } - virtual HistoryItem *getItem() const { + HistoryItem *getItem() const override { return _parent; } + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool active) override; + protected: HistoryItem *_parent; @@ -203,14 +202,23 @@ public: , _a_iconOver(animation(this, &LayoutRadialProgressItem::step_iconOver)) { } - void linkOver(const TextLinkPtr &lnk); - void linkOut(const TextLinkPtr &lnk); + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool active) override; ~LayoutRadialProgressItem(); protected: - TextLinkPtr _openl, _savel, _cancell; - void setLinks(ITextLink *openl, ITextLink *savel, ITextLink *cancell); + ClickHandlerPtr _openl, _savel, _cancell; + void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell); + void setDocumentLinks(DocumentData *document) { + ClickHandlerPtr save; + if (document->voice()) { + save.reset(new DocumentOpenClickHandler(document)); + } else { + save.reset(new DocumentSaveClickHandler(document)); + } + setLinks(MakeShared<DocumentOpenClickHandler>(document), std_::move(save), MakeShared<DocumentCancelClickHandler>(document)); + } void step_iconOver(float64 ms, bool timer); void step_radial(uint64 ms, bool timer); @@ -279,8 +287,8 @@ class LayoutOverviewDate : public LayoutItem { public: LayoutOverviewDate(const QDate &date, bool month); - virtual void initDimensions(); - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; + void initDimensions() override; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; private: QDate _date; @@ -292,14 +300,14 @@ class LayoutOverviewPhoto : public LayoutMediaItem { public: LayoutOverviewPhoto(PhotoData *photo, HistoryItem *parent); - virtual void initDimensions(); - virtual int32 resizeGetHeight(int32 width); - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void initDimensions() override; + int32 resizeGetHeight(int32 width) override; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; private: PhotoData *_data; - TextLinkPtr _link; + ClickHandlerPtr _link; mutable QPixmap _pix; mutable bool _goodLoaded; @@ -310,22 +318,22 @@ class LayoutOverviewVideo : public LayoutAbstractFileItem { public: LayoutOverviewVideo(DocumentData *video, HistoryItem *parent); - virtual void initDimensions(); - virtual int32 resizeGetHeight(int32 width); - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void initDimensions() override; + int32 resizeGetHeight(int32 width) override; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; protected: - virtual float64 dataProgress() const { + float64 dataProgress() const override { return _data->progress(); } - virtual bool dataFinished() const { + bool dataFinished() const override { return !_data->loading(); } - virtual bool dataLoaded() const { + bool dataLoaded() const override { return _data->loaded(); } - virtual bool iconAnimated() const { + bool iconAnimated() const override { return true; } @@ -344,27 +352,27 @@ class LayoutOverviewVoice : public LayoutAbstractFileItem { public: LayoutOverviewVoice(DocumentData *voice, HistoryItem *parent); - virtual void initDimensions(); - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void initDimensions() override; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; protected: - virtual float64 dataProgress() const { + float64 dataProgress() const override { return _data->progress(); } - virtual bool dataFinished() const { + bool dataFinished() const override { return !_data->loading(); } - virtual bool dataLoaded() const { + bool dataLoaded() const override { return _data->loaded(); } - virtual bool iconAnimated() const { + bool iconAnimated() const override { return true; } private: DocumentData *_data; - TextLinkPtr _namel; + ClickHandlerPtr _namel; mutable Text _name, _details; mutable int32 _nameVersion; @@ -378,31 +386,31 @@ class LayoutOverviewDocument : public LayoutAbstractFileItem { public: LayoutOverviewDocument(DocumentData *document, HistoryItem *parent); - virtual void initDimensions(); - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void initDimensions() override; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; virtual DocumentData *getDocument() const { return _data; } protected: - virtual float64 dataProgress() const { + float64 dataProgress() const override { return _data->progress(); } - virtual bool dataFinished() const { + bool dataFinished() const override { return !_data->loading(); } - virtual bool dataLoaded() const { + bool dataLoaded() const override { return _data->loaded(); } - virtual bool iconAnimated() const { + bool iconAnimated() const override { return _data->song() || !_data->loaded() || (_radial && _radial->animating()); } private: DocumentData *_data; - TextLinkPtr _msgl, _namel; + ClickHandlerPtr _msgl, _namel; mutable bool _thumbForLoaded; mutable QPixmap _thumb; @@ -422,13 +430,13 @@ class LayoutOverviewLink : public LayoutMediaItem { public: LayoutOverviewLink(HistoryMedia *media, HistoryItem *parent); - virtual void initDimensions(); - virtual int32 resizeGetHeight(int32 width); - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void initDimensions() override; + int32 resizeGetHeight(int32 width) override; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; private: - TextLinkPtr _photol; + ClickHandlerPtr _photol; QString _title, _letter; int _titlew = 0; @@ -443,7 +451,7 @@ private: Link(const QString &url, const QString &text); QString text; int32 width; - TextLinkPtr lnk; + TextClickHandlerPtr lnk; }; QVector<Link> _links; @@ -456,7 +464,7 @@ public: , paused(paused) , lastRow(lastRow) { } - virtual const InlinePaintContext *toInlinePaintContext() const { + const InlinePaintContext *toInlinePaintContext() const override { return this; } bool paused, lastRow; @@ -481,6 +489,14 @@ public: void update(); + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override { + update(); + } + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override { + update(); + } + protected: InlineResult *_result; DocumentData *_doc; @@ -490,22 +506,20 @@ protected: }; -class SendInlineItemLink : public ITextLink { - TEXT_LINK_CLASS(SendInlineItemLink) - +// this type used as a flag, we dynamic_cast<> to it +class SendInlineItemClickHandler : public ClickHandler { public: - virtual void onClick(Qt::MouseButton) const { + void onClick(Qt::MouseButton) const override { } - }; -class DeleteSavedGifLink : public ITextLink { - TEXT_LINK_CLASS(DeleteSavedGifLink) - +class DeleteSavedGifClickHandler : public LeftButtonClickHandler { public: - DeleteSavedGifLink(DocumentData *data) : _data(data) { + DeleteSavedGifClickHandler(DocumentData *data) : _data(data) { } - virtual void onClick(Qt::MouseButton) const; + +protected: + void onClickImpl() const override; private: DocumentData *_data; @@ -516,17 +530,19 @@ class LayoutInlineGif : public LayoutInlineItem { public: LayoutInlineGif(InlineResult *result, DocumentData *doc, bool saved); - virtual void setPosition(int32 position); - virtual void initDimensions(); + void setPosition(int32 position) override; + void initDimensions() override; - virtual bool fullLine() const { + bool fullLine() const override { return false; } - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; - virtual void linkOver(const TextLinkPtr &lnk); - virtual void linkOut(const TextLinkPtr &lnk); + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; + + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; ~LayoutInlineGif(); @@ -551,7 +567,7 @@ private: int32 _state; ClipReader *_gif; - TextLinkPtr _send, _delete; + ClickHandlerPtr _send, _delete; bool gif() const { return (!_gif || _gif == BadClipReader) ? false : true; } @@ -582,14 +598,14 @@ class LayoutInlinePhoto : public LayoutInlineItem { public: LayoutInlinePhoto(InlineResult *result, PhotoData *photo); - virtual void initDimensions(); + void initDimensions() override; - virtual bool fullLine() const { + bool fullLine() const override { return false; } - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; private: QSize countFrameSize() const; @@ -599,7 +615,7 @@ private: bool content_loaded() const; void content_forget(); - TextLinkPtr _send; + ClickHandlerPtr _send; mutable QPixmap _thumb; mutable bool _thumbLoaded; @@ -611,14 +627,14 @@ class LayoutInlineWebVideo : public LayoutInlineItem { public: LayoutInlineWebVideo(InlineResult *result); - virtual void initDimensions(); + void initDimensions() override; - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; private: - TextLinkPtr _send, _link; + ClickHandlerPtr _send, _link; mutable QPixmap _thumb; Text _title, _description; @@ -633,15 +649,15 @@ class LayoutInlineArticle : public LayoutInlineItem { public: LayoutInlineArticle(InlineResult *result, bool withThumb); - virtual void initDimensions(); - virtual int32 resizeGetHeight(int32 width); + void initDimensions() override; + int32 resizeGetHeight(int32 width) override; - virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const; - virtual void getState(TextLinkPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const; + void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override; + void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override; private: - TextLinkPtr _send, _url, _link; + ClickHandlerPtr _send, _url, _link; bool _withThumb; mutable QPixmap _thumb; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 2f2805d1e..9f4f3e0a5 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1470,8 +1470,12 @@ void MainWidget::stopAnimActive() { history.stopAnimActive(); } -void MainWidget::sendBotCommand(const QString &cmd, MsgId replyTo) { - history.sendBotCommand(cmd, replyTo); +void MainWidget::sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo) { + history.sendBotCommand(peer, cmd, replyTo); +} + +void MainWidget::sendBotCallback(PeerData *peer, const QString &cmd, MsgId replyTo) { + history.sendBotCallback(peer, cmd, replyTo); } bool MainWidget::insertBotCommand(const QString &cmd, bool specialGif) { @@ -2352,6 +2356,13 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, bool bac App::wnd()->getTitle()->updateBackButton(); } +PeerData *MainWidget::ui_getPeerForMouseAction() { + if (profile) { + return profile->ui_getPeerForMouseAction(); + } + return history.ui_getPeerForMouseAction(); +} + void MainWidget::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) { if (selectingPeer()) { outPeer = 0; diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 67c2398f0..ae20d3ca3 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -343,7 +343,8 @@ public: uint64 animActiveTimeStart(const HistoryItem *msg) const; void stopAnimActive(); - void sendBotCommand(const QString &cmd, MsgId msgId); + void sendBotCommand(PeerData *peer, const QString &cmd, MsgId replyTo); + void sendBotCallback(PeerData *peer, const QString &cmd, MsgId replyTo); bool insertBotCommand(const QString &cmd, bool specialGif); void searchMessages(const QString &query, PeerData *inPeer); @@ -438,6 +439,7 @@ public: bool ui_isInlineItemVisible(const LayoutInlineItem *layout); bool ui_isInlineItemBeingChosen(); void ui_showPeerHistory(quint64 peer, qint32 msgId, bool back); + PeerData *ui_getPeerForMouseAction(); void notify_botCommandsChanged(UserData *bot); void notify_inlineBotRequesting(bool requesting); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 853603304..5cedff00c 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -28,12 +28,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "gui/filedialog.h" namespace { - class SaveMsgLink : public ITextLink { - TEXT_LINK_CLASS(SaveMsgLink) - + class SaveMsgClickHandler : public ClickHandler { public: - SaveMsgLink(MediaView *view) : _view(view) { + SaveMsgClickHandler(MediaView *view) : _view(view) { } void onClick(Qt::MouseButton button) const { @@ -123,7 +121,7 @@ MediaView::MediaView() : TWidget(App::wnd()) custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); _saveMsgText.setRichText(st::medviewSaveMsgFont, lang(lng_mediaview_saved), _textDlgOptions, custom); _saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::medviewSaveMsgPadding.left() + st::medviewSaveMsgPadding.right(), st::medviewSaveMsgFont->height + st::medviewSaveMsgPadding.top() + st::medviewSaveMsgPadding.bottom()); - _saveMsgText.setLink(1, TextLinkPtr(new SaveMsgLink(this))); + _saveMsgText.setLink(1, MakeShared<SaveMsgClickHandler>(this)); _transparentBrush = QBrush(App::sprite().copy(st::mvTransparentBrush)); @@ -478,6 +476,15 @@ MediaView::~MediaView() { deleteAndMark(_menu); } +void MediaView::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + setCursor((active || ClickHandler::getPressed()) ? style::cur_pointer : style::cur_default); + update(QRegion(_saveMsg) + _captionRect); +} +void MediaView::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + setCursor((pressed || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); + update(QRegion(_saveMsg) + _captionRect); +} + void MediaView::showSaveMsgFile() { psShowInFolder(_saveMsgFilename); } @@ -565,7 +572,7 @@ void MediaView::onSaveAs() { if (_doc->data().isEmpty()) location.accessDisable(); } else { if (!fileShown()) { - DocumentSaveLink::doSave(_doc, true); + DocumentSaveClickHandler::doSave(_doc, true); updateControls(); } else { _saveVisible = false; @@ -594,7 +601,7 @@ void MediaView::onDocClick() { if (_doc->loading()) { onSaveCancel(); } else { - DocumentOpenLink::doOpen(_doc, ActionOnLoadNone); + DocumentOpenClickHandler::doOpen(_doc, ActionOnLoadNone); if (_doc->loading() && !_docRadial.animating()) { _docRadial.start(_doc->progress()); } @@ -624,6 +631,10 @@ void MediaView::clipCallback(ClipReaderNotification notification) { } } +PeerData *MediaView::ui_getPeerForMouseAction() { + return _history ? _history->peer : nullptr; +} + void MediaView::onDownload() { if (cAskDownloadPath()) { return onSaveAs(); @@ -649,7 +660,7 @@ void MediaView::onDownload() { location.accessDisable(); } else { if (!fileShown()) { - DocumentSaveLink::doSave(_doc); + DocumentSaveClickHandler::doSave(_doc); updateControls(); } else { _saveVisible = false; @@ -1631,13 +1642,11 @@ void MediaView::mousePressEvent(QMouseEvent *e) { updateOver(e->pos()); if (_menu || !_receiveMouse) return; - if (textlnkDown() != textlnkOver()) { - textlnkDown(textlnkOver()); - } + ClickHandler::pressed(); if (e->button() == Qt::LeftButton) { _down = OverNone; - if (!textlnkDown()) { + if (!ClickHandler::getPressed()) { if (_over == OverLeftNav && _index >= 0) { moveToNext(-1); _lastAction = e->pos(); @@ -1766,13 +1775,16 @@ bool MediaView::updateOverState(OverState newState) { } void MediaView::updateOver(QPoint pos) { - TextLinkPtr lnk; + ClickHandlerPtr lnk; + ClickHandlerHost *lnkhost = nullptr; bool inText; if (_saveMsgStarted && _saveMsg.contains(pos)) { _saveMsgText.getState(lnk, inText, pos.x() - _saveMsg.x() - st::medviewSaveMsgPadding.left(), pos.y() - _saveMsg.y() - st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right()); + lnkhost = this; } else if (_captionRect.contains(pos)) { _caption.getState(lnk, inText, pos.x() - _captionRect.x(), pos.y() - _captionRect.y(), _captionRect.width()); + lnkhost = this; } // retina @@ -1783,11 +1795,7 @@ void MediaView::updateOver(QPoint pos) { pos.setY(pos.y() - 1); } - if (lnk != textlnkOver()) { - textlnkOver(lnk); - setCursor((textlnkOver() || textlnkDown()) ? style::cur_pointer : style::cur_default); - update(QRegion(_saveMsg) + _captionRect); - } + ClickHandler::setActive(lnk, lnkhost); if (_pressed || _dragging) return; @@ -1816,21 +1824,12 @@ void MediaView::updateOver(QPoint pos) { void MediaView::mouseReleaseEvent(QMouseEvent *e) { updateOver(e->pos()); - TextLinkPtr lnk = textlnkDown(); - textlnkDown(TextLinkPtr()); - if (lnk && textlnkOver() == lnk) { - if (reHashtag().match(lnk->encoded()).hasMatch() && _history && _history->isChannel() && !_history->isMegagroup()) { - App::wnd()->hideMediaview(); - App::searchByHashtag(lnk->encoded(), _history->peer); - } else { - if (reBotCommand().match(lnk->encoded()).hasMatch() && _history) { - App::wnd()->hideMediaview(); - Ui::showPeerHistory(_history, ShowAtTheEndMsgId); - } - lnk->onClick(e->button()); - } + + if (ClickHandlerPtr activated = ClickHandler::unpressed()) { + App::activateClickHandler(activated, e->button()); return; } + if (_over == OverName && _down == OverName) { if (App::wnd() && _from) { close(); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 1736bcf10..51f585eff 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "dropdown.h" -class MediaView : public TWidget, public RPCSender { +class MediaView : public TWidget, public RPCSender, public ClickHandlerHost { Q_OBJECT public: @@ -30,7 +30,7 @@ public: MediaView(); void paintEvent(QPaintEvent *e); - + void keyPressEvent(QKeyEvent *e); void mousePressEvent(QMouseEvent *e); void mouseMoveEvent(QMouseEvent *e); @@ -72,9 +72,14 @@ public: void onDocClick(); void clipCallback(ClipReaderNotification notification); + PeerData *ui_getPeerForMouseAction(); ~MediaView(); + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; + public slots: void onHideControls(bool force = false); @@ -161,7 +166,7 @@ private: History *_migrated, *_history; // if conversation photos or files overview PeerData *_peer; UserData *_user; // if user profile photos overview - + PeerData *_from; Text _fromName; diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl index 120ad0b7f..204de380b 100644 --- a/Telegram/SourceFiles/mtproto/scheme.tl +++ b/Telegram/SourceFiles/mtproto/scheme.tl @@ -387,6 +387,7 @@ updateBotInlineQuery#c01eea08 query_id:long user_id:int query:string offset:stri updateBotInlineSend#f69e113 user_id:int query:string id:string = Update; updateEditChannelMessage#1b3f4df7 message:Message pts:int pts_count:int = Update; updateChannelPinnedMessage#98592475 channel_id:int id:int = Update; +updateBotCallbackQuery#5024c2b0 query_id:long user_id:int peer:Peer msg_id:int text:string = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -659,6 +660,8 @@ auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType; auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType; auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType; +messages.botCallbackAnswer#b4868d29 message:string = messages.BotCallbackAnswer; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -783,6 +786,8 @@ messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.getInlineBotResults#9324600d bot:InputUser query:string offset:string = messages.BotResults; messages.setInlineBotResults#3f23ec12 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string = Bool; messages.sendInlineBotResult#b16e06fe flags:# broadcast:flags.4?true silent:flags.5?true background:flags.6?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; +messages.getBotCallbackAnswer#d3157edf peer:InputPeer msg_id:int text:string = messages.BotCallbackAnswer; +messages.setBotCallbackAnswer#a13a9254 query_id:long message:string = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference; diff --git a/Telegram/SourceFiles/mtproto/scheme_auto.cpp b/Telegram/SourceFiles/mtproto/scheme_auto.cpp index 1fe0a87a4..40e427612 100644 --- a/Telegram/SourceFiles/mtproto/scheme_auto.cpp +++ b/Telegram/SourceFiles/mtproto/scheme_auto.cpp @@ -2946,6 +2946,23 @@ 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) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ updateBotCallbackQuery"); + 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(" text: "); ++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_updates_state(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); @@ -5339,6 +5356,19 @@ void _serialize_auth_sentCodeTypeFlashCall(MTPStringLogger &to, int32 stage, int } } +void _serialize_messages_botCallbackAnswer(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_botCallbackAnswer"); + to.add("\n").addSpaces(lev); + } + switch (stage) { + case 0: to.add(" message: "); ++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_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); @@ -5887,6 +5917,20 @@ void _serialize_messages_setInlineBotResults(MTPStringLogger &to, int32 stage, i } } +void _serialize_messages_setBotCallbackAnswer(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_setBotCallbackAnswer"); + 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(" message: "); ++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_upload_saveFilePart(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); @@ -7526,6 +7570,21 @@ void _serialize_messages_getInlineBotResults(MTPStringLogger &to, int32 stage, i } } +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) { + if (stage) { + to.add(",\n").addSpaces(lev); + } else { + to.add("{ messages_getBotCallbackAnswer"); + 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(" text: "); ++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_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(); } @@ -8034,6 +8093,7 @@ namespace { _serializers.insert(mtpc_updateBotInlineSend, _serialize_updateBotInlineSend); _serializers.insert(mtpc_updateEditChannelMessage, _serialize_updateEditChannelMessage); _serializers.insert(mtpc_updateChannelPinnedMessage, _serialize_updateChannelPinnedMessage); + _serializers.insert(mtpc_updateBotCallbackQuery, _serialize_updateBotCallbackQuery); _serializers.insert(mtpc_updates_state, _serialize_updates_state); _serializers.insert(mtpc_updates_differenceEmpty, _serialize_updates_differenceEmpty); _serializers.insert(mtpc_updates_difference, _serialize_updates_difference); @@ -8223,6 +8283,7 @@ namespace { _serializers.insert(mtpc_auth_sentCodeTypeSms, _serialize_auth_sentCodeTypeSms); _serializers.insert(mtpc_auth_sentCodeTypeCall, _serialize_auth_sentCodeTypeCall); _serializers.insert(mtpc_auth_sentCodeTypeFlashCall, _serialize_auth_sentCodeTypeFlashCall); + _serializers.insert(mtpc_messages_botCallbackAnswer, _serialize_messages_botCallbackAnswer); _serializers.insert(mtpc_req_pq, _serialize_req_pq); _serializers.insert(mtpc_req_DH_params, _serialize_req_DH_params); @@ -8265,6 +8326,7 @@ namespace { _serializers.insert(mtpc_messages_reorderStickerSets, _serialize_messages_reorderStickerSets); _serializers.insert(mtpc_messages_saveGif, _serialize_messages_saveGif); _serializers.insert(mtpc_messages_setInlineBotResults, _serialize_messages_setInlineBotResults); + _serializers.insert(mtpc_messages_setBotCallbackAnswer, _serialize_messages_setBotCallbackAnswer); _serializers.insert(mtpc_upload_saveFilePart, _serialize_upload_saveFilePart); _serializers.insert(mtpc_upload_saveBigFilePart, _serialize_upload_saveBigFilePart); _serializers.insert(mtpc_help_saveAppLog, _serialize_help_saveAppLog); @@ -8381,6 +8443,7 @@ namespace { _serializers.insert(mtpc_messages_searchGifs, _serialize_messages_searchGifs); _serializers.insert(mtpc_messages_getSavedGifs, _serialize_messages_getSavedGifs); _serializers.insert(mtpc_messages_getInlineBotResults, _serialize_messages_getInlineBotResults); + _serializers.insert(mtpc_messages_getBotCallbackAnswer, _serialize_messages_getBotCallbackAnswer); _serializers.insert(mtpc_updates_getState, _serialize_updates_getState); _serializers.insert(mtpc_updates_getDifference, _serialize_updates_getDifference); _serializers.insert(mtpc_updates_getChannelDifference, _serialize_updates_getChannelDifference); diff --git a/Telegram/SourceFiles/mtproto/scheme_auto.h b/Telegram/SourceFiles/mtproto/scheme_auto.h index e945eba47..203ae4768 100644 --- a/Telegram/SourceFiles/mtproto/scheme_auto.h +++ b/Telegram/SourceFiles/mtproto/scheme_auto.h @@ -281,6 +281,7 @@ enum { mtpc_updateBotInlineSend = 0xf69e113, mtpc_updateEditChannelMessage = 0x1b3f4df7, mtpc_updateChannelPinnedMessage = 0x98592475, + mtpc_updateBotCallbackQuery = 0x5024c2b0, mtpc_updates_state = 0xa56c2a3e, mtpc_updates_differenceEmpty = 0x5d75a138, mtpc_updates_difference = 0xf49ca0, @@ -470,6 +471,7 @@ enum { mtpc_auth_sentCodeTypeSms = 0xc000bba2, mtpc_auth_sentCodeTypeCall = 0x5353e5a7, mtpc_auth_sentCodeTypeFlashCall = 0xab03c6d9, + mtpc_messages_botCallbackAnswer = 0xb4868d29, mtpc_invokeAfterMsg = 0xcb9f372d, mtpc_invokeAfterMsgs = 0x3dc4b4f0, mtpc_initConnection = 0x69796de9, @@ -587,6 +589,8 @@ enum { mtpc_messages_getInlineBotResults = 0x9324600d, mtpc_messages_setInlineBotResults = 0x3f23ec12, mtpc_messages_sendInlineBotResult = 0xb16e06fe, + mtpc_messages_getBotCallbackAnswer = 0xd3157edf, + mtpc_messages_setBotCallbackAnswer = 0xa13a9254, mtpc_updates_getState = 0xedd4882a, mtpc_updates_getDifference = 0xa041495, mtpc_updates_getChannelDifference = 0xbb32d7c0, @@ -976,6 +980,7 @@ class MTPDupdateBotInlineQuery; class MTPDupdateBotInlineSend; class MTPDupdateEditChannelMessage; class MTPDupdateChannelPinnedMessage; +class MTPDupdateBotCallbackQuery; class MTPupdates_state; class MTPDupdates_state; @@ -1280,6 +1285,9 @@ class MTPDauth_sentCodeTypeSms; class MTPDauth_sentCodeTypeCall; class MTPDauth_sentCodeTypeFlashCall; +class MTPmessages_botCallbackAnswer; +class MTPDmessages_botCallbackAnswer; + // Boxed types definitions typedef MTPBoxed<MTPresPQ> MTPResPQ; @@ -1446,6 +1454,7 @@ typedef MTPBoxed<MTPmessageFwdHeader> MTPMessageFwdHeader; typedef MTPBoxed<MTPchannels_messageEditData> MTPchannels_MessageEditData; typedef MTPBoxed<MTPauth_codeType> MTPauth_CodeType; typedef MTPBoxed<MTPauth_sentCodeType> MTPauth_SentCodeType; +typedef MTPBoxed<MTPmessages_botCallbackAnswer> MTPmessages_BotCallbackAnswer; // Type classes definitions @@ -5410,6 +5419,18 @@ public: return *(const MTPDupdateChannelPinnedMessage*)data; } + MTPDupdateBotCallbackQuery &_updateBotCallbackQuery() { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_updateBotCallbackQuery) throw mtpErrorWrongTypeId(_type, mtpc_updateBotCallbackQuery); + split(); + return *(MTPDupdateBotCallbackQuery*)data; + } + const MTPDupdateBotCallbackQuery &c_updateBotCallbackQuery() const { + if (!data) throw mtpErrorUninitialized(); + if (_type != mtpc_updateBotCallbackQuery) throw mtpErrorWrongTypeId(_type, mtpc_updateBotCallbackQuery); + return *(const MTPDupdateBotCallbackQuery*)data; + } + uint32 innerLength() const; mtpTypeId type() const; void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons); @@ -5462,6 +5483,7 @@ private: explicit MTPupdate(MTPDupdateBotInlineSend *_data); explicit MTPupdate(MTPDupdateEditChannelMessage *_data); explicit MTPupdate(MTPDupdateChannelPinnedMessage *_data); + explicit MTPupdate(MTPDupdateBotCallbackQuery *_data); friend class MTP::internal::TypeCreator; @@ -9016,6 +9038,37 @@ private: }; typedef MTPBoxed<MTPauth_sentCodeType> MTPauth_SentCodeType; +class MTPmessages_botCallbackAnswer : private mtpDataOwner { +public: + MTPmessages_botCallbackAnswer(); + MTPmessages_botCallbackAnswer(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_botCallbackAnswer) : mtpDataOwner(0) { + read(from, end, cons); + } + + MTPDmessages_botCallbackAnswer &_messages_botCallbackAnswer() { + if (!data) throw mtpErrorUninitialized(); + split(); + return *(MTPDmessages_botCallbackAnswer*)data; + } + const MTPDmessages_botCallbackAnswer &c_messages_botCallbackAnswer() const { + if (!data) throw mtpErrorUninitialized(); + return *(const MTPDmessages_botCallbackAnswer*)data; + } + + uint32 innerLength() const; + mtpTypeId type() const; + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_botCallbackAnswer); + void write(mtpBuffer &to) const; + + typedef void ResponseType; + +private: + explicit MTPmessages_botCallbackAnswer(MTPDmessages_botCallbackAnswer *_data); + + friend class MTP::internal::TypeCreator; +}; +typedef MTPBoxed<MTPmessages_botCallbackAnswer> MTPmessages_BotCallbackAnswer; + // Type constructors with data class MTPDresPQ : public mtpDataImpl<MTPDresPQ> { @@ -11375,6 +11428,20 @@ public: MTPint vid; }; +class MTPDupdateBotCallbackQuery : public mtpDataImpl<MTPDupdateBotCallbackQuery> { +public: + MTPDupdateBotCallbackQuery() { + } + MTPDupdateBotCallbackQuery(const MTPlong &_query_id, MTPint _user_id, const MTPPeer &_peer, MTPint _msg_id, const MTPstring &_text) : vquery_id(_query_id), vuser_id(_user_id), vpeer(_peer), vmsg_id(_msg_id), vtext(_text) { + } + + MTPlong vquery_id; + MTPint vuser_id; + MTPPeer vpeer; + MTPint vmsg_id; + MTPstring vtext; +}; + class MTPDupdates_state : public mtpDataImpl<MTPDupdates_state> { public: MTPDupdates_state() { @@ -13438,6 +13505,16 @@ public: MTPstring vpattern; }; +class MTPDmessages_botCallbackAnswer : public mtpDataImpl<MTPDmessages_botCallbackAnswer> { +public: + MTPDmessages_botCallbackAnswer() { + } + MTPDmessages_botCallbackAnswer(const MTPstring &_message) : vmessage(_message) { + } + + MTPstring vmessage; +}; + // RPC methods class MTPreq_pq { // RPC method 'req_pq' @@ -18871,6 +18948,93 @@ public: } }; +class MTPmessages_getBotCallbackAnswer { // RPC method 'messages.getBotCallbackAnswer' +public: + MTPInputPeer vpeer; + MTPint vmsg_id; + MTPstring vtext; + + MTPmessages_getBotCallbackAnswer() { + } + MTPmessages_getBotCallbackAnswer(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getBotCallbackAnswer) { + read(from, end, cons); + } + MTPmessages_getBotCallbackAnswer(const MTPInputPeer &_peer, MTPint _msg_id, const MTPstring &_text) : vpeer(_peer), vmsg_id(_msg_id), vtext(_text) { + } + + uint32 innerLength() const { + return vpeer.innerLength() + vmsg_id.innerLength() + vtext.innerLength(); + } + mtpTypeId type() const { + return mtpc_messages_getBotCallbackAnswer; + } + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_getBotCallbackAnswer) { + vpeer.read(from, end); + vmsg_id.read(from, end); + vtext.read(from, end); + } + void write(mtpBuffer &to) const { + vpeer.write(to); + vmsg_id.write(to); + vtext.write(to); + } + + typedef MTPmessages_BotCallbackAnswer ResponseType; +}; +class MTPmessages_GetBotCallbackAnswer : public MTPBoxed<MTPmessages_getBotCallbackAnswer> { +public: + MTPmessages_GetBotCallbackAnswer() { + } + MTPmessages_GetBotCallbackAnswer(const MTPmessages_getBotCallbackAnswer &v) : MTPBoxed<MTPmessages_getBotCallbackAnswer>(v) { + } + MTPmessages_GetBotCallbackAnswer(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_getBotCallbackAnswer>(from, end, cons) { + } + MTPmessages_GetBotCallbackAnswer(const MTPInputPeer &_peer, MTPint _msg_id, const MTPstring &_text) : MTPBoxed<MTPmessages_getBotCallbackAnswer>(MTPmessages_getBotCallbackAnswer(_peer, _msg_id, _text)) { + } +}; + +class MTPmessages_setBotCallbackAnswer { // RPC method 'messages.setBotCallbackAnswer' +public: + MTPlong vquery_id; + MTPstring vmessage; + + MTPmessages_setBotCallbackAnswer() { + } + MTPmessages_setBotCallbackAnswer(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_setBotCallbackAnswer) { + read(from, end, cons); + } + MTPmessages_setBotCallbackAnswer(const MTPlong &_query_id, const MTPstring &_message) : vquery_id(_query_id), vmessage(_message) { + } + + uint32 innerLength() const { + return vquery_id.innerLength() + vmessage.innerLength(); + } + mtpTypeId type() const { + return mtpc_messages_setBotCallbackAnswer; + } + void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_messages_setBotCallbackAnswer) { + vquery_id.read(from, end); + vmessage.read(from, end); + } + void write(mtpBuffer &to) const { + vquery_id.write(to); + vmessage.write(to); + } + + typedef MTPBool ResponseType; +}; +class MTPmessages_SetBotCallbackAnswer : public MTPBoxed<MTPmessages_setBotCallbackAnswer> { +public: + MTPmessages_SetBotCallbackAnswer() { + } + MTPmessages_SetBotCallbackAnswer(const MTPmessages_setBotCallbackAnswer &v) : MTPBoxed<MTPmessages_setBotCallbackAnswer>(v) { + } + MTPmessages_SetBotCallbackAnswer(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed<MTPmessages_setBotCallbackAnswer>(from, end, cons) { + } + MTPmessages_SetBotCallbackAnswer(const MTPlong &_query_id, const MTPstring &_message) : MTPBoxed<MTPmessages_setBotCallbackAnswer>(MTPmessages_setBotCallbackAnswer(_query_id, _message)) { + } +}; + class MTPupdates_getState { // RPC method 'updates.getState' public: MTPupdates_getState() { @@ -21694,6 +21858,9 @@ public: inline static MTPupdate new_updateChannelPinnedMessage(MTPint _channel_id, MTPint _id) { return MTPupdate(new MTPDupdateChannelPinnedMessage(_channel_id, _id)); } + inline static MTPupdate new_updateBotCallbackQuery(const MTPlong &_query_id, MTPint _user_id, const MTPPeer &_peer, MTPint _msg_id, const MTPstring &_text) { + return MTPupdate(new MTPDupdateBotCallbackQuery(_query_id, _user_id, _peer, _msg_id, _text)); + } inline static MTPupdates_state new_updates_state(MTPint _pts, MTPint _qts, MTPint _date, MTPint _seq, MTPint _unread_count) { return MTPupdates_state(new MTPDupdates_state(_pts, _qts, _date, _seq, _unread_count)); } @@ -22261,6 +22428,9 @@ public: inline static MTPauth_sentCodeType new_auth_sentCodeTypeFlashCall(const MTPstring &_pattern) { return MTPauth_sentCodeType(new MTPDauth_sentCodeTypeFlashCall(_pattern)); } + inline static MTPmessages_botCallbackAnswer new_messages_botCallbackAnswer(const MTPstring &_message) { + return MTPmessages_botCallbackAnswer(new MTPDmessages_botCallbackAnswer(_message)); + } }; } // namespace internal @@ -27159,6 +27329,10 @@ inline uint32 MTPupdate::innerLength() const { const MTPDupdateChannelPinnedMessage &v(c_updateChannelPinnedMessage()); return v.vchannel_id.innerLength() + v.vid.innerLength(); } + case mtpc_updateBotCallbackQuery: { + const MTPDupdateBotCallbackQuery &v(c_updateBotCallbackQuery()); + return v.vquery_id.innerLength() + v.vuser_id.innerLength() + v.vpeer.innerLength() + v.vmsg_id.innerLength() + v.vtext.innerLength(); + } } return 0; } @@ -27458,6 +27632,15 @@ inline void MTPupdate::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeI v.vchannel_id.read(from, end); v.vid.read(from, end); } break; + case mtpc_updateBotCallbackQuery: _type = cons; { + if (!data) setData(new MTPDupdateBotCallbackQuery()); + MTPDupdateBotCallbackQuery &v(_updateBotCallbackQuery()); + v.vquery_id.read(from, end); + v.vuser_id.read(from, end); + v.vpeer.read(from, end); + v.vmsg_id.read(from, end); + v.vtext.read(from, end); + } break; default: throw mtpErrorUnexpected(cons, "MTPupdate"); } } @@ -27707,6 +27890,14 @@ inline void MTPupdate::write(mtpBuffer &to) const { v.vchannel_id.write(to); v.vid.write(to); } break; + case mtpc_updateBotCallbackQuery: { + const MTPDupdateBotCallbackQuery &v(c_updateBotCallbackQuery()); + v.vquery_id.write(to); + v.vuser_id.write(to); + v.vpeer.write(to); + v.vmsg_id.write(to); + v.vtext.write(to); + } break; } } inline MTPupdate::MTPupdate(mtpTypeId type) : mtpDataOwner(0), _type(type) { @@ -27756,6 +27947,7 @@ inline MTPupdate::MTPupdate(mtpTypeId type) : mtpDataOwner(0), _type(type) { case mtpc_updateBotInlineSend: setData(new MTPDupdateBotInlineSend()); break; case mtpc_updateEditChannelMessage: setData(new MTPDupdateEditChannelMessage()); break; case mtpc_updateChannelPinnedMessage: setData(new MTPDupdateChannelPinnedMessage()); break; + case mtpc_updateBotCallbackQuery: setData(new MTPDupdateBotCallbackQuery()); break; default: throw mtpErrorBadTypeId(type, "MTPupdate"); } } @@ -27845,6 +28037,8 @@ inline MTPupdate::MTPupdate(MTPDupdateEditChannelMessage *_data) : mtpDataOwner( } inline MTPupdate::MTPupdate(MTPDupdateChannelPinnedMessage *_data) : mtpDataOwner(_data), _type(mtpc_updateChannelPinnedMessage) { } +inline MTPupdate::MTPupdate(MTPDupdateBotCallbackQuery *_data) : mtpDataOwner(_data), _type(mtpc_updateBotCallbackQuery) { +} inline MTPupdate MTP_updateNewMessage(const MTPMessage &_message, MTPint _pts, MTPint _pts_count) { return MTP::internal::TypeCreator::new_updateNewMessage(_message, _pts, _pts_count); } @@ -27981,6 +28175,9 @@ inline MTPupdate MTP_updateEditChannelMessage(const MTPMessage &_message, MTPint inline MTPupdate MTP_updateChannelPinnedMessage(MTPint _channel_id, MTPint _id) { return MTP::internal::TypeCreator::new_updateChannelPinnedMessage(_channel_id, _id); } +inline MTPupdate MTP_updateBotCallbackQuery(const MTPlong &_query_id, MTPint _user_id, const MTPPeer &_peer, MTPint _msg_id, const MTPstring &_text) { + return MTP::internal::TypeCreator::new_updateBotCallbackQuery(_query_id, _user_id, _peer, _msg_id, _text); +} inline MTPupdates_state::MTPupdates_state() : mtpDataOwner(new MTPDupdates_state()) { } @@ -32871,6 +33068,33 @@ inline MTPauth_sentCodeType MTP_auth_sentCodeTypeCall(MTPint _length) { inline MTPauth_sentCodeType MTP_auth_sentCodeTypeFlashCall(const MTPstring &_pattern) { return MTP::internal::TypeCreator::new_auth_sentCodeTypeFlashCall(_pattern); } + +inline MTPmessages_botCallbackAnswer::MTPmessages_botCallbackAnswer() : mtpDataOwner(new MTPDmessages_botCallbackAnswer()) { +} + +inline uint32 MTPmessages_botCallbackAnswer::innerLength() const { + const MTPDmessages_botCallbackAnswer &v(c_messages_botCallbackAnswer()); + return v.vmessage.innerLength(); +} +inline mtpTypeId MTPmessages_botCallbackAnswer::type() const { + return mtpc_messages_botCallbackAnswer; +} +inline void MTPmessages_botCallbackAnswer::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons) { + if (cons != mtpc_messages_botCallbackAnswer) throw mtpErrorUnexpected(cons, "MTPmessages_botCallbackAnswer"); + + if (!data) setData(new MTPDmessages_botCallbackAnswer()); + MTPDmessages_botCallbackAnswer &v(_messages_botCallbackAnswer()); + v.vmessage.read(from, end); +} +inline void MTPmessages_botCallbackAnswer::write(mtpBuffer &to) const { + const MTPDmessages_botCallbackAnswer &v(c_messages_botCallbackAnswer()); + v.vmessage.write(to); +} +inline MTPmessages_botCallbackAnswer::MTPmessages_botCallbackAnswer(MTPDmessages_botCallbackAnswer *_data) : mtpDataOwner(_data) { +} +inline MTPmessages_botCallbackAnswer MTP_messages_botCallbackAnswer(const MTPstring &_message) { + return MTP::internal::TypeCreator::new_messages_botCallbackAnswer(_message); +} inline MTPDmessage::Flags mtpCastFlags(MTPDmessageService::Flags flags) { return MTPDmessage::Flags(QFlag(flags)); } inline MTPDmessage::Flags mtpCastFlags(MTPflags<MTPDmessageService::Flags> flags) { return mtpCastFlags(flags.v); } inline MTPDmessage::Flags mtpCastFlags(MTPDupdateShortMessage::Flags flags) { return MTPDmessage::Flags(QFlag(flags)); } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index df371894b..57aa800e6 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -474,12 +474,7 @@ void OverviewInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton but dragActionUpdate(screenPos); if (button != Qt::LeftButton) return; - if (textlnkDown() != textlnkOver()) { - repaintItem(App::pressedLinkItem()); - textlnkDown(textlnkOver()); - App::pressedLinkItem(App::hoveredLinkItem()); - repaintItem(App::pressedLinkItem()); - } + ClickHandler::pressed(); _dragAction = NoDrag; _dragItem = _mousedItem; @@ -487,11 +482,11 @@ void OverviewInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton but _dragStartPos = mapMouseToItem(mapFromGlobal(screenPos), _dragItem, _dragItemIndex); _dragWasInactive = App::wnd()->inactivePress(); if (_dragWasInactive) App::wnd()->inactivePress(false); - if (textlnkDown() && _selected.isEmpty()) { + if (ClickHandler::getPressed() && _selected.isEmpty()) { _dragAction = PrepareDrag; } else if (!_selected.isEmpty()) { if (_selected.cbegin().value() == FullSelection) { - if (_selected.constFind(_dragItem) != _selected.cend() && textlnkDown()) { + if (_selected.constFind(_dragItem) != _selected.cend() && ClickHandler::getPressed()) { _dragAction = PrepareDrag; // start items drag } else { _dragAction = PrepareSelect; // start items select @@ -499,27 +494,8 @@ void OverviewInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton but } } if (_dragAction == NoDrag && _dragItem) { - bool afterDragSymbol = false , uponSymbol = false; - uint16 symbol = 0; if (!_dragWasInactive) { - if (textlnkDown()) { - _dragSymbol = symbol; - uint32 selStatus = (_dragSymbol << 16) | _dragSymbol; - if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) { - if (!_selected.isEmpty()) { - repaintItem(_selected.cbegin().key(), -1); - _selected.clear(); - } - _selected.insert(_dragItem, selStatus); - _dragAction = Selecting; - repaintItem(_dragItem, _dragItemIndex); - _overview->updateTopBarSelection(); - } else { - _dragAction = PrepareSelect; - } - } else { - _dragAction = PrepareSelect; // start items select - } + _dragAction = PrepareSelect; } } @@ -541,31 +517,22 @@ void OverviewInner::dragActionCancel() { } void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton button) { - TextLinkPtr needClick; - dragActionUpdate(screenPos); - if (textlnkOver()) { - if (textlnkDown() == textlnkOver() && _dragAction != Dragging && !_selMode) { - needClick = textlnkDown(); - } + ClickHandlerPtr activated = ClickHandler::unpressed(); + if (_dragAction == Dragging || _selMode) { + activated.clear(); } - if (textlnkDown()) { - repaintItem(App::pressedLinkItem()); - textlnkDown(TextLinkPtr()); - App::pressedLinkItem(0); - if (!textlnkOver() && _cursor != style::cur_default) { - _cursor = style::cur_default; - setCursor(_cursor); - } + if (!ClickHandler::getActive() && _cursor != style::cur_default) { + _cursor = style::cur_default; + setCursor(_cursor); } - if (needClick) { - DEBUG_LOG(("Will click link: %1 (%2) %3").arg(needClick->text()).arg(needClick->readable()).arg(needClick->encoded())); + if (activated) { dragActionCancel(); - App::activateTextLink(needClick, button); + App::activateClickHandler(activated, button); return; } - if (_dragAction == PrepareSelect && !needClick && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { + if (_dragAction == PrepareSelect && !_dragWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { SelectedItems::iterator i = _selected.find(_dragItem); if (i == _selected.cend() && itemMsgId(_dragItem) > 0) { if (_selected.size() < MaxSelectedItems) { @@ -578,7 +545,7 @@ void OverviewInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton bu _selected.erase(i); } repaintItem(_dragItem, _dragItemIndex); - } else if (_dragAction == PrepareDrag && !needClick && !_dragWasInactive && button != Qt::RightButton) { + } else if (_dragAction == PrepareDrag && !_dragWasInactive && button != Qt::RightButton) { SelectedItems::iterator i = _selected.find(_dragItem); if (i != _selected.cend() && i.value() == FullSelection) { _selected.erase(i); @@ -619,16 +586,17 @@ void OverviewInner::onDragExec() { uponSelected = false; } } + ClickHandlerPtr pressedHandler = ClickHandler::getPressed(); QString sel; QList<QUrl> urls; bool forwardSelected = false; if (uponSelected) { forwardSelected = !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && !Adaptive::OneColumn(); - } else if (textlnkDown()) { - sel = textlnkDown()->encoded(); - if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') { -// urls.push_back(QUrl::fromEncoded(sel.toUtf8())); // Google Chrome crashes in Mac OS X O_o - } + } else if (pressedHandler) { + sel = pressedHandler->dragText(); + //if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') { + // urls.push_back(QUrl::fromEncoded(sel.toUtf8())); // Google Chrome crashes in Mac OS X O_o + //} } if (!sel.isEmpty() || forwardSelected) { updateDragSelection(0, -1, 0, -1, false); @@ -647,19 +615,22 @@ void OverviewInner::onDragExec() { if (App::main()) App::main()->updateAfterDrag(); return; } else { - HistoryItem *pressedLnkItem = App::pressedLinkItem(), *pressedItem = App::pressedItem(); - QLatin1String lnkType = (textlnkDown() && pressedLnkItem) ? textlnkDown()->type() : qstr(""); - bool lnkPhoto = (lnkType == qstr("PhotoLink")), - lnkVideo = (lnkType == qstr("VideoOpenLink")), - lnkAudio = (lnkType == qstr("AudioOpenLink")), - lnkDocument = (lnkType == qstr("DocumentOpenLink") || lnkType == qstr("GifOpenLink")); - if (lnkPhoto || lnkVideo || lnkAudio || lnkDocument) { + QString forwardMimeType; + HistoryMedia *pressedMedia = nullptr; + if (HistoryItem *pressedLnkItem = App::pressedLinkItem()) { + if ((pressedMedia = pressedLnkItem->getMedia())) { + if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) { + forwardMimeType = qsl("application/x-td-forward-pressed-link"); + } + } + } + if (!forwardMimeType.isEmpty()) { QDrag *drag = new QDrag(App::wnd()); QMimeData *mimeData = new QMimeData; mimeData->setData(qsl("application/x-td-forward-pressed-link"), "1"); - if (lnkDocument) { - QString filepath = static_cast<DocumentOpenLink*>(textlnkDown().data())->document()->filepath(DocumentData::FilePathResolveChecked); + if (DocumentData *document = (pressedMedia ? pressedMedia->getDocument() : nullptr)) { + QString filepath = document->filepath(DocumentData::FilePathResolveChecked); if (!filepath.isEmpty()) { QList<QUrl> urls; urls.push_back(QUrl::fromLocalFile(filepath)); @@ -901,7 +872,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { } void OverviewInner::mouseMoveEvent(QMouseEvent *e) { - if (!(e->buttons() & (Qt::LeftButton | Qt::MiddleButton)) && (textlnkDown() || _dragAction != NoDrag)) { + if (!(e->buttons() & (Qt::LeftButton | Qt::MiddleButton)) && _dragAction != NoDrag) { mouseReleaseEvent(e); } dragActionUpdate(e->globalPos()); @@ -913,7 +884,8 @@ void OverviewInner::onUpdateSelected() { QPoint mousePos(mapFromGlobal(_dragPos)); QPoint m(_overview->clampMousePosition(mousePos)); - TextLinkPtr lnk; + ClickHandlerPtr lnk; + ClickHandlerHost *lnkhost = nullptr; HistoryItem *item = 0; int32 index = -1; int32 newsel = 0; @@ -941,6 +913,7 @@ void OverviewInner::onUpdateSelected() { index = i; if (upon) { media->getState(lnk, cursorState, m.x() - col * w - st::overviewPhotoSkip, m.y() - _marginTop - row * vsize - st::overviewPhotoSkip); + lnkhost = media; } } } @@ -976,6 +949,7 @@ void OverviewInner::onUpdateSelected() { item = media->getItem(); index = i; media->getState(lnk, cursorState, m.x() - _rowsLeft, m.y() - _marginTop - top); + lnkhost = media; } break; } @@ -989,37 +963,15 @@ void OverviewInner::onUpdateSelected() { m = mapMouseToItem(m, _mousedItem, _mousedItemIndex); Qt::CursorShape cur = style::cur_default; - bool lnkChanged = false; - if (lnk != textlnkOver()) { - lnkChanged = true; - if (textlnkOver()) { - if (HistoryItem *item = App::hoveredLinkItem()) { - MsgId itemId = complexMsgId(item); - int32 itemIndex = oldMousedItemIndex; - fixItemIndex(itemIndex, itemId); - if (itemIndex >= 0) { - _items.at(itemIndex)->linkOut(textlnkOver()); - repaintItem(itemId, itemIndex); - } - } - } - textlnkOver(lnk); + bool lnkChanged = ClickHandler::setActive(lnk, lnkhost); + if (lnkChanged) { PopupTooltip::Hide(); - App::hoveredLinkItem(lnk ? item : 0); - if (textlnkOver()) { - if (item && index >= 0) { - _items.at(index)->linkOver(textlnkOver()); - repaintItem(complexMsgId(item), index); - } - } - } else { - App::mousedItem(item); } + App::mousedItem(item); if (_mousedItem != oldMousedItem) { - lnkChanged = true; + PopupTooltip::Hide(); if (oldMousedItem) repaintItem(oldMousedItem, oldMousedItemIndex); if (item) repaintItem(item); - PopupTooltip::Hide(); } if (_cursorState == HistoryInDateCursorState && cursorState != HistoryInDateCursorState) { PopupTooltip::Hide(); @@ -1050,7 +1002,6 @@ void OverviewInner::onUpdateSelected() { _dragAction = Selecting; } } - cur = textlnkDown() ? style::cur_pointer : style::cur_default; if (_dragAction == Selecting) { bool canSelectMany = (_peer != 0); if (_mousedItem == _dragItem && lnk && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { @@ -1123,7 +1074,7 @@ void OverviewInner::onUpdateSelected() { } else if (_dragAction == Dragging) { } - if (textlnkDown()) { + if (ClickHandler::getPressed()) { cur = style::cur_pointer; } else if (_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { if (!_dragSelFrom || !_dragSelTo) { @@ -1148,13 +1099,12 @@ QPoint OverviewInner::tooltipPos() const { } QString OverviewInner::tooltipText() const { - TextLinkPtr lnk = textlnkOver(); - if (lnk && !lnk->fullDisplayed()) { - return lnk->readable(); - } else if (_cursorState == HistoryInDateCursorState && _dragAction == NoDrag && _mousedItem) { + if (_cursorState == HistoryInDateCursorState && _dragAction == NoDrag && _mousedItem) { if (HistoryItem *item = App::histItemById(itemChannel(_mousedItem), itemMsgId(_mousedItem))) { return item->date.toString(QLocale::system().dateTimeFormat(QLocale::LongFormat)); } + } else if (ClickHandlerPtr lnk = ClickHandler::getActive()) { + return lnk->tooltip(); } return QString(); } @@ -1208,14 +1158,10 @@ void OverviewInner::leaveEvent(QEvent *e) { repaintItem(_selectedMsgId, -1); _selectedMsgId = 0; } - if (textlnkOver()) { - repaintItem(App::hoveredLinkItem()); - textlnkOver(TextLinkPtr()); - App::hoveredLinkItem(0); - if (!textlnkDown() && _cursor != style::cur_default) { - _cursor = style::cur_default; - setCursor(_cursor); - } + ClickHandler::clearActive(); + if (!ClickHandler::getPressed() && _cursor != style::cur_default) { + _cursor = style::cur_default; + setCursor(_cursor); } return QWidget::leaveEvent(e); } @@ -1264,9 +1210,9 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { isUponSelected = hasSelected; } - _contextMenuLnk = textlnkOver(); - PhotoLink *lnkPhoto = dynamic_cast<PhotoLink*>(_contextMenuLnk.data()); - DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data()); + _contextMenuLnk = ClickHandler::getActive(); + PhotoClickHandler *lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLnk.data()); + DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data()); bool lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideo() : false; bool lnkIsAudio = lnkDocument ? (lnkDocument->document()->voice() != nullptr) : false; bool lnkIsSong = lnkDocument ? (lnkDocument->document()->song() != nullptr) : false; @@ -1310,16 +1256,9 @@ void OverviewInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (_selectedMsgId) repaintItem(_selectedMsgId, -1); } else if (!ignoreMousedItem && App::mousedItem() && App::mousedItem()->channelId() == itemChannel(_mousedItem) && App::mousedItem()->id == itemMsgId(_mousedItem)) { _menu = new PopupMenu(); - QLatin1String linktype = _contextMenuLnk ? _contextMenuLnk->type() : qstr(""); - if (linktype == qstr("TextLink") || linktype == qstr("LocationLink")) { - _menu->addAction(lang(lng_context_copy_link), this, SLOT(copyContextUrl()))->setEnabled(true); - } else if (linktype == qstr("EmailLink")) { - _menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true); - } else if (linktype == qstr("MentionLink")) { - _menu->addAction(lang(lng_context_copy_mention), this, SLOT(copyContextUrl()))->setEnabled(true); - } else if (linktype == qstr("HashtagLink")) { - _menu->addAction(lang(lng_context_copy_hashtag), this, SLOT(copyContextUrl()))->setEnabled(true); - } else { + QString copyToClipboardContextItem = _contextMenuLnk ? _contextMenuLnk->copyToClipboardContextItem() : QString(); + if (!copyToClipboardContextItem.isEmpty()) { + _menu->addAction(copyToClipboardContextItem, this, SLOT(copyContextUrl()))->setEnabled(true); } _menu->addAction(lang(lng_context_to_msg), this, SLOT(goToMessage()))->setEnabled(true); if (isUponSelected > 1) { @@ -1447,9 +1386,8 @@ void OverviewInner::setSelectMode(bool enabled) { } void OverviewInner::copyContextUrl() { - QString enc = _contextMenuLnk ? _contextMenuLnk->encoded() : QString(); - if (!enc.isEmpty()) { - QApplication::clipboard()->setText(enc); + if (_contextMenuLnk) { + _contextMenuLnk->copyToClipboard(); } } @@ -1494,14 +1432,14 @@ void OverviewInner::selectMessage() { } void OverviewInner::cancelContextDownload() { - DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data()); + DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data()); if (lnkDocument) { lnkDocument->document()->cancel(); } } void OverviewInner::showContextInFolder() { - if (DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data())) { + if (DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data())) { QString filepath = lnkDocument->document()->filepath(DocumentData::FilePathResolveChecked); if (!filepath.isEmpty()) { psShowInFolder(filepath); @@ -1510,8 +1448,8 @@ void OverviewInner::showContextInFolder() { } void OverviewInner::saveContextFile() { - DocumentLink *lnkDocument = dynamic_cast<DocumentLink*>(_contextMenuLnk.data()); - if (lnkDocument) DocumentSaveLink::doSave(lnkDocument->document(), true); + DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLnk.data()); + if (lnkDocument) DocumentSaveClickHandler::doSave(lnkDocument->document(), true); } bool OverviewInner::onSearchMessages(bool searchCache) { diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index 2b13596be..6cd7affbb 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -76,9 +76,9 @@ public: void clearSelectedItems(bool onlyTextSelection = false); void fillSelectedItems(SelectedItemSet &sel, bool forDelete = true); - // AbstractTooltipShower - virtual QString tooltipText() const; - virtual QPoint tooltipPos() const; + // AbstractTooltipShower interface + QString tooltipText() const override; + QPoint tooltipPos() const override; ~OverviewInner(); @@ -217,7 +217,7 @@ private: uint16 _dragSymbol; bool _dragWasInactive; - TextLinkPtr _contextMenuLnk; + ClickHandlerPtr _contextMenuLnk; MsgId _dragSelFrom, _dragSelTo; int32 _dragSelFromIndex, _dragSelToIndex; diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp index eefeb4da0..85bc5c732 100644 --- a/Telegram/SourceFiles/playerwidget.cpp +++ b/Telegram/SourceFiles/playerwidget.cpp @@ -325,7 +325,7 @@ void PlayerWidget::preloadNext() { if (HistoryDocument *document = static_cast<HistoryDocument*>(next->getMedia())) { DocumentData *d = document->getDocument(); if (!d->loaded(DocumentData::FilePathResolveSaveFromDataSilent)) { - DocumentOpenLink::doOpen(d, ActionOnLoadNone); + DocumentOpenClickHandler::doOpen(d, ActionOnLoadNone); } } } diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index a3036db88..dcfc9ad3e 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -117,7 +117,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData _phoneText = App::formatPhone(_peerUser->phone.isEmpty() ? App::phoneFromSharedContact(peerToUser(_peerUser->id)) : _peerUser->phone); PhotoData *userPhoto = (_peerUser->photoId && _peerUser->photoId != UnknownPeerPhotoId) ? App::photo(_peerUser->photoId) : 0; if (userPhoto && userPhoto->date) { - _photoLink = TextLinkPtr(new PhotoLink(userPhoto, _peer)); + _photoLink.reset(new PhotoOpenClickHandler(userPhoto, _peer)); } if ((_peerUser->botInfo && !_peerUser->botInfo->inited) || (_peerUser->photoId == UnknownPeerPhotoId) || (_peerUser->photoId && !userPhoto->date) || (_peerUser->blocked == UserBlockUnknown)) { if (App::api()) App::api()->requestFullPeer(_peer); @@ -125,7 +125,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData } else if (_peerChat) { PhotoData *chatPhoto = (_peerChat->photoId && _peerChat->photoId != UnknownPeerPhotoId) ? App::photo(_peerChat->photoId) : 0; if (chatPhoto && chatPhoto->date) { - _photoLink = TextLinkPtr(new PhotoLink(chatPhoto, _peer)); + _photoLink.reset(new PhotoOpenClickHandler(chatPhoto, _peer)); } if (_peerChat->photoId == UnknownPeerPhotoId) { if (App::api()) App::api()->requestFullPeer(_peer); @@ -133,7 +133,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData } else if (_peerChannel) { PhotoData *chatPhoto = (_peerChannel->photoId && _peerChannel->photoId != UnknownPeerPhotoId) ? App::photo(_peerChannel->photoId) : 0; if (chatPhoto && chatPhoto->date) { - _photoLink = TextLinkPtr(new PhotoLink(chatPhoto, _peer)); + _photoLink.reset(new PhotoOpenClickHandler(chatPhoto, _peer)); } bool needAdmins = (_peerChannel->isMegagroup() && _peerChannel->amEditor()), adminsOutdated = (_peerChannel->isMegagroup() && (_peerChannel->mgInfo->lastParticipantsStatus & MegagroupInfo::LastParticipantsAdminsOutdated)); if (_peerChannel->isMegagroup() && (_peerChannel->mgInfo->lastParticipants.isEmpty() || (needAdmins && adminsOutdated) || _peerChannel->lastParticipantsCountOutdated())) { @@ -532,9 +532,9 @@ void ProfileInner::onFullPeerUpdated(PeerData *peer) { if (_peerUser) { PhotoData *userPhoto = (_peerUser->photoId && _peerUser->photoId != UnknownPeerPhotoId) ? App::photo(_peerUser->photoId) : 0; if (userPhoto && userPhoto->date) { - _photoLink = TextLinkPtr(new PhotoLink(userPhoto, _peer)); + _photoLink.reset(new PhotoOpenClickHandler(userPhoto, _peer)); } else { - _photoLink = TextLinkPtr(); + _photoLink.clear(); } if (_peerUser) { if (_peerUser->about.isEmpty()) { @@ -573,7 +573,7 @@ void ProfileInner::onBotSettings() { QString cmd = _peerUser->botInfo->commands.at(i).command; if (!cmd.compare(qsl("settings"), Qt::CaseInsensitive)) { Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); - App::main()->sendBotCommand('/' + cmd, 0); + App::sendBotCommand(_peerUser, '/' + cmd); return; } } @@ -587,7 +587,7 @@ void ProfileInner::onBotHelp() { QString cmd = _peerUser->botInfo->commands.at(i).command; if (!cmd.compare(qsl("help"), Qt::CaseInsensitive)) { Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); - App::main()->sendBotCommand('/' + cmd, 0); + App::sendBotCommand(_peerUser, '/' + cmd); return; } } @@ -627,7 +627,11 @@ void ProfileInner::peerUpdated(PeerData *data) { _onlineText = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status); updatePinnedMessageVisibility(); } - _photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo, _peer)) : TextLinkPtr(); + if (photo && photo->date) { + _photoLink.reset(new PhotoOpenClickHandler(photo, _peer)); + } else { + _photoLink.clear(); + } if (_peer->name != _nameCache) { _nameCache = _peer->name; _nameText.setText(st::profileNameFont, _nameCache, _textNameOptions); @@ -1093,26 +1097,33 @@ void ProfileInner::mouseMoveEvent(QMouseEvent *e) { } } if (!_photoLink && (_peerUser || (_peerChat && !_peerChat->canEdit()) || (_peerChannel && !_amCreator))) { - setCursor((_kickOver || _kickDown || textlnkOver()) ? style::cur_pointer : style::cur_default); + setCursor((_kickOver || _kickDown || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } else { - setCursor((_kickOver || _kickDown || _photoOver || textlnkOver()) ? style::cur_pointer : style::cur_default); + setCursor((_kickOver || _kickDown || _photoOver || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } } +void ProfileInner::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { + update(QRect(_left, _aboutTop, _width, _aboutHeight)); +} + +void ProfileInner::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + update(QRect(_left, _aboutTop, _width, _aboutHeight)); +} + void ProfileInner::updateSelected() { if (!isVisible()) return; QPoint lp = mapFromGlobal(_lastPos); - TextLinkPtr lnk; + ClickHandlerPtr lnk; + ClickHandlerHost *lnkhost = nullptr; bool inText = false; if (!_about.isEmpty() && lp.y() >= _aboutTop && lp.y() < _aboutTop + _aboutHeight && lp.x() >= _left && lp.x() < _left + _width) { _about.getState(lnk, inText, lp.x() - _left, lp.y() - _aboutTop, _width); + lnkhost = this; } - if (textlnkOver() != lnk) { - textlnkOver(lnk); - update(QRect(_left, _aboutTop, _width, _aboutHeight)); - } + ClickHandler::setActive(lnk, lnkhost); int32 participantsTop = 0; if (canDeleteChannel()) { @@ -1150,6 +1161,9 @@ void ProfileInner::updateSelected() { void ProfileInner::mousePressEvent(QMouseEvent *e) { _lastPos = e->globalPos(); updateSelected(); + + ClickHandler::pressed(); + if (e->button() == Qt::LeftButton) { if (_kickOver) { _kickDown = _kickOver; @@ -1163,7 +1177,6 @@ void ProfileInner::mousePressEvent(QMouseEvent *e) { onUpdatePhoto(); } } - textlnkDown(textlnkOver()); } } @@ -1179,25 +1192,14 @@ void ProfileInner::mouseReleaseEvent(QMouseEvent *e) { _kickDown = 0; if (!_photoLink && (_peerUser || (_peerChat && !_peerChat->canEdit()) || (_peerChannel && !_amCreator))) { - setCursor((_kickOver || _kickDown || textlnkOver()) ? style::cur_pointer : style::cur_default); + setCursor((_kickOver || _kickDown || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } else { - setCursor((_kickOver || _kickDown || _photoOver || textlnkOver()) ? style::cur_pointer : style::cur_default); + setCursor((_kickOver || _kickDown || _photoOver || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } update(); - if (textlnkDown()) { - TextLinkPtr lnk = textlnkDown(); - textlnkDown(TextLinkPtr()); - if (lnk == textlnkOver()) { - if (reHashtag().match(lnk->encoded()).hasMatch() && _peerChannel) { - App::searchByHashtag(lnk->encoded(), _peerChannel); - } else { - if (reBotCommand().match(lnk->encoded()).hasMatch()) { - Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); - } - App::activateTextLink(lnk, e->button()); - } - } + if (ClickHandlerPtr activated = ClickHandler::unpressed()) { + App::activateClickHandler(activated, e->button()); } } @@ -2056,6 +2058,10 @@ void ProfileWidget::updateAdaptiveLayout() { _sideShadow.setVisible(!Adaptive::OneColumn()); } +PeerData *ProfileWidget::ui_getPeerForMouseAction() { + return _inner.peer(); +} + void ProfileWidget::clear() { if (_inner.peer() && _inner.peer()->isUser() && _inner.peer()->asUser()->botInfo) { _inner.peer()->asUser()->botInfo->startGroupToken = QString(); diff --git a/Telegram/SourceFiles/profilewidget.h b/Telegram/SourceFiles/profilewidget.h index fe27c90e5..8f3676cdb 100644 --- a/Telegram/SourceFiles/profilewidget.h +++ b/Telegram/SourceFiles/profilewidget.h @@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once class ProfileWidget; -class ProfileInner : public TWidget, public RPCSender { +class ProfileInner : public TWidget, public RPCSender, public ClickHandlerHost { Q_OBJECT public: @@ -66,6 +66,10 @@ public: ~ProfileInner(); + // ClickHandlerHost interface + void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override; + public slots: void peerUpdated(PeerData *data); @@ -158,7 +162,7 @@ private: Text _nameText; QString _nameCache; QString _phoneText; - TextLinkPtr _photoLink; + ClickHandlerPtr _photoLink; FlatButton _uploadPhoto, _addParticipant; FlatButton _sendMessage, _shareContact, _inviteToGroup; LinkButton _cancelPhoto, _createInvitationLink, _invitationLink; @@ -271,6 +275,8 @@ public: RPCSender::rpcClear(); } + PeerData *ui_getPeerForMouseAction(); + void clear(); ~ProfileWidget(); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index 6eb623270..538ea1cfc 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -215,7 +215,9 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent) _nameText.setText(st::setNameFont, _nameCache, _textNameOptions); PhotoData *selfPhoto = (self()->photoId && self()->photoId != UnknownPeerPhotoId) ? App::photo(self()->photoId) : 0; - if (selfPhoto && selfPhoto->date) _photoLink = TextLinkPtr(new PhotoLink(selfPhoto, self())); + if (selfPhoto && selfPhoto->date) { + _photoLink.reset(new PhotoOpenClickHandler(selfPhoto, self())); + } App::api()->requestFullPeer(self()); onReloadPassword(); @@ -354,13 +356,13 @@ void SettingsInner::peerUpdated(PeerData *data) { if (self()->photoId && self()->photoId != UnknownPeerPhotoId) { PhotoData *selfPhoto = App::photo(self()->photoId); if (selfPhoto->date) { - _photoLink = TextLinkPtr(new PhotoLink(selfPhoto, self())); + _photoLink.reset(new PhotoOpenClickHandler(selfPhoto, self())); } else { - _photoLink = TextLinkPtr(); + _photoLink.clear(); App::api()->requestFullPeer(self()); } } else { - _photoLink = TextLinkPtr(); + _photoLink.clear(); } if (_nameCache != self()->name) { @@ -940,9 +942,9 @@ void SettingsInner::onFullPeerUpdated(PeerData *peer) { PhotoData *selfPhoto = (self()->photoId && self()->photoId != UnknownPeerPhotoId) ? App::photo(self()->photoId) : 0; if (selfPhoto && selfPhoto->date) { - _photoLink = TextLinkPtr(new PhotoLink(selfPhoto, self())); + _photoLink.reset(new PhotoOpenClickHandler(selfPhoto, self())); } else { - _photoLink = TextLinkPtr(); + _photoLink.clear(); } } diff --git a/Telegram/SourceFiles/settingswidget.h b/Telegram/SourceFiles/settingswidget.h index 667921ba4..5d0954891 100644 --- a/Telegram/SourceFiles/settingswidget.h +++ b/Telegram/SourceFiles/settingswidget.h @@ -206,7 +206,7 @@ private: // profile Text _nameText; QString _nameCache; - TextLinkPtr _photoLink; + ClickHandlerPtr _photoLink; FlatButton _uploadPhoto; LinkButton _cancelPhoto; bool _nameOver, _photoOver; diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 122380730..a60fce862 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -99,7 +99,6 @@ NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats; NotifySettingsPtr globalNotifyAllPtr = UnknownNotifySettings, globalNotifyUsersPtr = UnknownNotifySettings, globalNotifyChatsPtr = UnknownNotifySettings; PeerData::PeerData(const PeerId &id) : id(id) -, lnk(new PeerLink(this)) , loadedStatus(NotLoaded) , colorIndex(peerColorIndex(id)) , color(peerColor(colorIndex)) @@ -684,24 +683,18 @@ PhotoData::~PhotoData() { deleteAndMark(uploadingData); } -void PhotoLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton) { - App::wnd()->showPhoto(this, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); - } +void PhotoOpenClickHandler::onClickImpl() const { + App::wnd()->showPhoto(this, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); } -void PhotoSaveLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; - +void PhotoSaveClickHandler::onClickImpl() const { PhotoData *data = photo(); if (!data->date) return; data->download(); } -void PhotoCancelLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; - +void PhotoCancelClickHandler::onClickImpl() const { PhotoData *data = photo(); if (!data->date) return; @@ -857,7 +850,7 @@ QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = fals return saveFileName(caption, filter, prefix, name, forceSavingAs, dir); } -void DocumentOpenLink::doOpen(DocumentData *data, ActionOnLoad action) { +void DocumentOpenClickHandler::doOpen(DocumentData *data, ActionOnLoad action) { if (!data->date) return; HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0); @@ -933,22 +926,15 @@ void DocumentOpenLink::doOpen(DocumentData *data, ActionOnLoad action) { data->save(filename, action, item ? item->fullId() : FullMsgId()); } -void DocumentOpenLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; +void DocumentOpenClickHandler::onClickImpl() const { doOpen(document(), document()->voice() ? ActionOnLoadNone : ActionOnLoadOpen); } -void VoiceSaveLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; - doOpen(document(), ActionOnLoadNone); -} - -void GifOpenLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; +void GifOpenClickHandler::onClickImpl() const { doOpen(document(), ActionOnLoadPlayInline); } -void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) { +void DocumentSaveClickHandler::doSave(DocumentData *data, bool forceSavingAs) { if (!data->date) return; QString filepath = data->filepath(DocumentData::FilePathResolveSaveFromDataSilent, forceSavingAs); @@ -970,14 +956,11 @@ void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) { } } -void DocumentSaveLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; +void DocumentSaveClickHandler::onClickImpl() const { doSave(document()); } -void DocumentCancelLink::onClick(Qt::MouseButton button) const { - if (button != Qt::LeftButton) return; - +void DocumentCancelClickHandler::onClickImpl() const { DocumentData *data = document(); if (!data->date) return; @@ -1557,8 +1540,8 @@ InlineResult::~InlineResult() { cancelFile(); } -void PeerLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton && App::main()) { +void PeerOpenClickHandler::onClickImpl() const { + if (App::main()) { if (peer() && peer()->isChannel() && App::main()->historyPeer() != peer()) { if (!peer()->asChannel()->isPublic() && !peer()->asChannel()->amIn()) { Ui::showLayer(new InformBox(lang((peer()->isMegagroup()) ? lng_group_not_accessible : lng_channel_not_accessible))); @@ -1571,22 +1554,6 @@ void PeerLink::onClick(Qt::MouseButton button) const { } } -void MessageLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton && App::main()) { - HistoryItem *current = App::mousedItem(); - if (current && current->history()->peer->id == peer()) { - App::main()->pushReplyReturn(current); - } - Ui::showPeerHistory(peer(), msgid()); - } -} - -void CommentsLink::onClick(Qt::MouseButton button) const { - if (button == Qt::LeftButton && App::main() && _item->history()->isChannel()) { - Ui::showPeerHistoryAtItem(_item); - } -} - MsgId clientMsgId() { static MsgId currentClientMsgId = StartClientMsgId; Q_ASSERT(currentClientMsgId < EndClientMsgId); diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 6546a8c03..5a5e7def6 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -213,22 +213,40 @@ inline const QString &emptyUsername() { return empty; } +class PeerClickHandler : public LeftButtonClickHandler { +public: + PeerClickHandler(PeerData *peer) : _peer(peer) { + } + PeerData *peer() const { + return _peer; + } + +private: + PeerData *_peer; + +}; + +class PeerOpenClickHandler : public PeerClickHandler { +public: + using PeerClickHandler::PeerClickHandler; +protected: + void onClickImpl() const override; +}; + class UserData; class ChatData; class ChannelData; + class PeerData { protected: - PeerData(const PeerId &id); PeerData(const PeerData &other) = delete; PeerData &operator=(const PeerData &other) = delete; public: - virtual ~PeerData() { if (notify != UnknownNotifySettings && notify != EmptyNotifySettings) { - delete notify; - notify = UnknownNotifySettings; + deleteAndMark(notify); } } @@ -270,8 +288,6 @@ public: return int32(uint32(id & 0xFFFFFFFFULL)); } - TextLinkPtr lnk; - QString name; Text nameText; typedef QSet<QString> Names; @@ -315,29 +331,24 @@ public: return QString(); } -protected: + const ClickHandlerPtr &openLink() { + if (!_openLink) { + _openLink.reset(new PeerOpenClickHandler(this)); + } + return _openLink; + } +protected: ImagePtr _userpic; ImagePtr currentUserpic() const; + +private: + ClickHandlerPtr _openLink; + }; static const uint64 UserNoAccess = 0xFFFFFFFFFFFFFFFFULL; -class PeerLink : public ITextLink { - TEXT_LINK_CLASS(PeerLink) - -public: - PeerLink(PeerData *peer) : _peer(peer) { - } - void onClick(Qt::MouseButton button) const; - PeerData *peer() const { - return _peer; - } - -private: - PeerData *_peer; -}; - class BotCommand { public: BotCommand(const QString &command, const QString &description) : command(command), _description(description) { @@ -885,13 +896,10 @@ private: }; -class PhotoLink : public ITextLink { - TEXT_LINK_CLASS(PhotoLink) - +class PhotoClickHandler : public LeftButtonClickHandler { public: - PhotoLink(PhotoData *photo, PeerData *peer = 0) : _photo(photo), _peer(peer) { + PhotoClickHandler(PhotoData *photo, PeerData *peer = 0) : _photo(photo), _peer(peer) { } - void onClick(Qt::MouseButton button) const; PhotoData *photo() const { return _photo; } @@ -905,24 +913,25 @@ private: }; -class PhotoSaveLink : public PhotoLink { - TEXT_LINK_CLASS(PhotoSaveLink) - +class PhotoOpenClickHandler : public PhotoClickHandler { public: - PhotoSaveLink(PhotoData *photo, PeerData *peer = 0) : PhotoLink(photo, peer) { - } - void onClick(Qt::MouseButton button) const; - + using PhotoClickHandler::PhotoClickHandler; +protected: + void onClickImpl() const override; }; -class PhotoCancelLink : public PhotoLink { - TEXT_LINK_CLASS(PhotoCancelLink) - +class PhotoSaveClickHandler : public PhotoClickHandler { public: - PhotoCancelLink(PhotoData *photo, PeerData *peer = 0) : PhotoLink(photo, peer) { - } - void onClick(Qt::MouseButton button) const; + using PhotoClickHandler::PhotoClickHandler; +protected: + void onClickImpl() const override; +}; +class PhotoCancelClickHandler : public PhotoClickHandler { +public: + using PhotoClickHandler::PhotoClickHandler; +protected: + void onClickImpl() const override; }; enum FileStatus { @@ -1160,11 +1169,9 @@ inline bool operator!=(const AudioMsgId &a, const AudioMsgId &b) { return !(a == b); } -class DocumentLink : public ITextLink { - TEXT_LINK_CLASS(DocumentLink) - +class DocumentClickHandler : public LeftButtonClickHandler { public: - DocumentLink(DocumentData *document) : _document(document) { + DocumentClickHandler(DocumentData *document) : _document(document) { } DocumentData *document() const { return _document; @@ -1175,56 +1182,34 @@ private: }; -class DocumentSaveLink : public DocumentLink { - TEXT_LINK_CLASS(DocumentSaveLink) - +class DocumentSaveClickHandler : public DocumentClickHandler { public: - DocumentSaveLink(DocumentData *document) : DocumentLink(document) { - } + using DocumentClickHandler::DocumentClickHandler; static void doSave(DocumentData *document, bool forceSavingAs = false); - void onClick(Qt::MouseButton button) const; - +protected: + void onClickImpl() const override; }; -class DocumentOpenLink : public DocumentLink { - TEXT_LINK_CLASS(DocumentOpenLink) - +class DocumentOpenClickHandler : public DocumentClickHandler { public: - DocumentOpenLink(DocumentData *document) : DocumentLink(document) { - } + using DocumentClickHandler::DocumentClickHandler; static void doOpen(DocumentData *document, ActionOnLoad action = ActionOnLoadOpen); - void onClick(Qt::MouseButton button) const; - +protected: + void onClickImpl() const override; }; -class VoiceSaveLink : public DocumentOpenLink { - TEXT_LINK_CLASS(VoiceSaveLink) - +class GifOpenClickHandler : public DocumentOpenClickHandler { public: - VoiceSaveLink(DocumentData *document) : DocumentOpenLink(document) { - } - void onClick(Qt::MouseButton button) const; - + using DocumentOpenClickHandler::DocumentOpenClickHandler; +protected: + void onClickImpl() const override; }; -class GifOpenLink : public DocumentOpenLink { - TEXT_LINK_CLASS(GifOpenLink) - +class DocumentCancelClickHandler : public DocumentClickHandler { public: - GifOpenLink(DocumentData *document) : DocumentOpenLink(document) { - } - void onClick(Qt::MouseButton button) const; - -}; - -class DocumentCancelLink : public DocumentLink { - TEXT_LINK_CLASS(DocumentCancelLink) - -public: - DocumentCancelLink(DocumentData *document) : DocumentLink(document) { - } - void onClick(Qt::MouseButton button) const; - + using DocumentClickHandler::DocumentClickHandler; +protected: + void onClickImpl() const override; }; enum WebPageType { diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index df9fd6d27..a6a776a3e 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -775,6 +775,11 @@ inline UniquePointer<T> MakeUnique(Args&&... args) { return UniquePointer<T>(new T(std_::forward<Args>(args)...)); } +template <typename T, class... Args> +inline QSharedPointer<T> MakeShared(Args&&... args) { + return QSharedPointer<T>(new T(std_::forward<Args>(args)...)); +} + template <typename I> inline void destroyImplementation(I *&ptr) { if (ptr) { diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index d2ce17a4f..3299275fc 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -792,7 +792,7 @@ PasscodeWidget *Window::passcodeWidget() { return _passcode; } -void Window::showPhoto(const PhotoLink *lnk, HistoryItem *item) { +void Window::showPhoto(const PhotoOpenClickHandler *lnk, HistoryItem *item) { return lnk->peer() ? showPhoto(lnk->photo(), lnk->peer()) : showPhoto(lnk->photo(), item); } @@ -879,6 +879,15 @@ void Window::ui_hideStickerPreview() { _stickerPreview->hidePreview(); } +PeerData *Window::ui_getPeerForMouseAction() { + if (_mediaView && !_mediaView->isHidden()) { + return _mediaView->ui_getPeerForMouseAction(); + } else if (main) { + return main->ui_getPeerForMouseAction(); + } + return nullptr; +} + void Window::showConnecting(const QString &text, const QString &reconnect) { if (_connecting) { _connecting->set(text, reconnect); @@ -1736,8 +1745,8 @@ void Window::notifyUpdateAllPhotos() { if (_mediaView && !_mediaView->isHidden()) _mediaView->updateControls(); } -void Window::app_activateTextLink(TextLinkPtr link, Qt::MouseButton button) { - link->onClick(button); +void Window::app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button) { + handler->onClick(button); } void Window::notifyUpdateAll() { diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index cecad8cf7..0c7f589c0 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -180,7 +180,7 @@ public: void hideConnecting(); bool connectingVisible() const; - void showPhoto(const PhotoLink *lnk, HistoryItem *item = 0); + void showPhoto(const PhotoOpenClickHandler *lnk, HistoryItem *item = 0); void showPhoto(PhotoData *photo, HistoryItem *item); void showPhoto(PhotoData *photo, PeerData *item); void showDocument(DocumentData *doc, HistoryItem *item); @@ -241,6 +241,7 @@ public: bool ui_isMediaViewShown(); void ui_showStickerPreview(DocumentData *sticker); void ui_hideStickerPreview(); + PeerData *ui_getPeerForMouseAction(); public slots: @@ -283,7 +284,7 @@ public slots: void notifyUpdateAllPhotos(); - void app_activateTextLink(TextLinkPtr link, Qt::MouseButton button); + void app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button); signals: