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(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(); + qRegisterMetaType(); qRegisterMetaType(); - 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(this)->_bookmark.reset(0); + const_cast(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; - 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 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 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 TextBlocks; TextBlocks _blocks; - typedef QVector TextLinks; + typedef QVector 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()) { + 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()) { + 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()) { + 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(_data), MakeShared(_data), MakeShared(_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(_data, chat), MakeShared(_data, chat), MakeShared(_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(_data), MakeShared(_data), MakeShared(_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()) { - 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(media)->fname(); - QString lname = static_cast(media)->lname(); - QString phone = static_cast(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(media)->fname(); + QString lname = static_cast(media)->lname(); + QString phone = static_cast(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()) { 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()) { reply->clearData(this); } } void HistoryService::setMessageByAction(const MTPmessageAction &action) { - QList links; + QList 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(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(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(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(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(_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(); 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(_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(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 { mutable QString _text; mutable int _width = 0; mutable int _maxWidth = 0; - TextLinkPtr _lnk; + ClickHandlerPtr _lnk; }; struct HistoryMessageViews : public BaseComponent { @@ -1037,13 +1037,13 @@ struct HistoryMessageReply : public BaseComponent { 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 { }; 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