diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 6e14cfeb33..41a87c7ebd 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -118,7 +118,7 @@ void ConfirmBox::updateHover() { QPoint m(mapFromGlobal(_lastMousePos)); textstyleSet(&st::boxTextStyle); - 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); + ClickHandlerPtr handler = _text.linkLeft(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, width(), style::al_left); textstyleRestore(); ClickHandler::setActive(handler, this); @@ -183,11 +183,7 @@ ConfirmLinkBox::ConfirmLinkBox(const QString &url) : ConfirmBox(lang(lng_open_th void ConfirmLinkBox::onOpenLink() { Ui::hideLayer(); - if (reMailStart().match(_url).hasMatch()) { - EmailClickHandler::doOpen(_url); - } else { - UrlClickHandler::doOpen(_url); - } + UrlClickHandler::doOpen(_url); } MaxInviteBox::MaxInviteBox(const QString &link) : AbstractBox(st::boxWidth) diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 3e0cf92125..ac8fc41d79 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -33,6 +33,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "confirmbox.h" +QString cantInviteError() { + return lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.me/spambot"), lang(lng_cant_more_info))); +} + ContactsInner::ContactsInner(CreatingGroupType creating) : TWidget() , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _newItemHeight(creating == CreatingGroupNone ? st::contactsNewItemHeight : 0) @@ -1776,7 +1780,7 @@ bool ContactsBox::creationFail(const RPCError &error) { _filter.showError(); return true; } else if (error.type() == "PEER_FLOOD") { - Ui::showLayer(new InformBox(lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info)))), KeepOtherLayers); + Ui::showLayer(new InformBox(cantInviteError()), KeepOtherLayers); return true; } else if (error.type() == qstr("USER_RESTRICTED")) { Ui::showLayer(new InformBox(lang(lng_cant_do_this))); diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 6f9eed12d1..87d7ad8072 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -28,6 +28,8 @@ enum MembersFilter { }; typedef QMap MembersAlreadyIn; +QString cantInviteError(); + class ConfirmBox; class ContactsInner : public TWidget, public RPCSender { Q_OBJECT @@ -51,7 +53,7 @@ public: void mouseMoveEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); void resizeEvent(QResizeEvent *e); - + void paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel); void updateFilter(QString filter = QString()); @@ -136,7 +138,7 @@ private: UserData *_addAdmin; mtpRequestId _addAdminRequestId; ConfirmBox *_addAdminBox; - + int32 _time; DialogsIndexed *_contacts; diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index a84eba91b7..482ad7a2d9 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -808,10 +808,8 @@ public: } else { lnk.reset(new HashtagClickHandler(data.url)); } - } else if (data.fullDisplayed < 0) { // email - lnk.reset(new EmailClickHandler(data.url)); - } else { - lnk.reset(new UrlClickHandler(data.url, data.fullDisplayed > 0)); + } else { // email or url + lnk.reset(new UrlClickHandler(data.url, data.fullDisplayed != 0)); } _t->setLink(lnkIndex, lnk); } @@ -959,52 +957,64 @@ namespace { } QString UrlClickHandler::copyToClipboardContextItem() const { - return lang(lng_context_copy_link); + return lang(isEmail() ? lng_context_copy_email : lng_context_copy_link); } -void UrlClickHandler::doOpen(QString url) { - PopupTooltip::Hide(); +namespace { +QString tryConvertUrlToLocal(const QString &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)); + return qsl("tg://join?invite=") + myUrlEncode(telegramMeGroup.captured(1)); } else if (telegramMeStickers.hasMatch()) { - url = qsl("tg://addstickers?set=") + myUrlEncode(telegramMeStickers.captured(1)); + return qsl("tg://addstickers?set=") + myUrlEncode(telegramMeStickers.captured(1)); } else if (telegramMeShareUrl.hasMatch()) { - url = qsl("tg://msg_url?") + telegramMeShareUrl.captured(1); + return 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); + return qsl("tg://resolve/?domain=") + myUrlEncode(telegramMeUser.captured(1)) + postParam + (params.isEmpty() ? QString() : '&' + params); + } + return url; +} + +} // namespace + +void UrlClickHandler::doOpen(QString url) { + PopupTooltip::Hide(); + + if (isEmail(url)) { + QUrl u(qstr("mailto:") + url); + if (!QDesktopServices::openUrl(u)) { + psOpenFile(u.toString(QUrl::FullyEncoded), true); + } + return; } - if (QRegularExpression(qsl("^tg://[a-zA-Z0-9]+"), QRegularExpression::CaseInsensitiveOption).match(url).hasMatch()) { + url = tryConvertUrlToLocal(url); + + if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { App::openLocalUrl(url); } else { QDesktopServices::openUrl(url); } } -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 HiddenUrlClickHandler::onClick(Qt::MouseButton button) const { - Ui::showLayer(new ConfirmLinkBox(url())); + QString u = url(); + + u = tryConvertUrlToLocal(u); + + if (u.startsWith(qstr("tg://"))) { + App::openLocalUrl(u); + } else { + Ui::showLayer(new ConfirmLinkBox(u)); + } } QString LocationClickHandler::copyToClipboardContextItem() const { @@ -1333,10 +1343,11 @@ public: } const ClickHandlerPtr &link(int32 x, int32 y, int32 w, style::align align) { - static const ClickHandlerPtr *zero = new ClickHandlerPtr(); // won't be deleted + StaticNeverFreedPointer zero(new ClickHandlerPtr()); + _lnkX = x; _lnkY = y; - _lnkResult = zero; + _lnkResult = zero.data(); if (!_t->isNull() && _lnkX >= 0 && _lnkX < w && _lnkY >= 0) { draw(0, 0, w, align, _lnkY, _lnkY + 1); } diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index d2edb51940..684774740d 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -481,8 +481,12 @@ protected: 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; + if (isEmail()) { + _readable = _url; + } else { + QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); + _readable = good.isValid() ? good.toDisplayString() : _url; + } } QString copyToClipboardContextItem() const override; @@ -502,6 +506,10 @@ public: protected: QString url() const override { + if (isEmail()) { + return _url; + } + QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _url); @@ -515,6 +523,14 @@ protected: } private: + static bool isEmail(const QString &url) { + int at = url.indexOf('@'), slash = url.indexOf('/'); + return ((at > 0) && (slash < 0 || slash > at)); + } + bool isEmail() const { + return isEmail(_url); + } + QString _url, _readable; }; @@ -528,41 +544,6 @@ public: }; -class EmailClickHandler : public TextClickHandler { -public: - EmailClickHandler(const QString &email) : _email(email) { - } - QString copyToClipboardContextItem() const override; - - QString text() const override { - return _email; - } - - static void doOpen(QString email); - void onClick(Qt::MouseButton button) const override { - if (button == Qt::LeftButton || button == Qt::MiddleButton) { - doOpen(_email); - } - } - -protected: - QString url() const override { - return _email; - } - -private: - QString _email; - -}; - -inline TextClickHandlerPtr clickHandlerFromUrl(const QString &url) { - int32 at = url.indexOf('@'), slash = url.indexOf('/'); - if ((at > 0) && (slash < 0 || slash > at)) { - return MakeShared(url); - } - return MakeShared(url); -} - struct LocationCoords { LocationCoords() : lat(0), lon(0) { } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index 5e7af361a2..25047919a5 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -138,14 +138,14 @@ QString ItemBase::getResultUrl() const { ClickHandlerPtr ItemBase::getResultUrlHandler() const { if (!_result->_url.isEmpty()) { - return clickHandlerFromUrl(_result->_url); + return MakeShared(_result->_url); } return ClickHandlerPtr(); } ClickHandlerPtr ItemBase::getResultContentUrlHandler() const { if (!_result->_content_url.isEmpty()) { - return clickHandlerFromUrl(_result->_content_url); + return MakeShared(_result->_content_url); } return ClickHandlerPtr(); } diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index 48ba91258b..16ccee54dc 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -1109,17 +1109,17 @@ LayoutOverviewLink::LayoutOverviewLink(HistoryMedia *media, HistoryItem *parent) _photol.reset(new DocumentOpenClickHandler(_page->doc)); } else if (_page->photo) { if (_page->type == WebPageProfile || _page->type == WebPageVideo) { - _photol = clickHandlerFromUrl(_page->url); + _photol = MakeShared(_page->url); } else if (_page->type == WebPagePhoto || _page->siteName == qstr("Twitter") || _page->siteName == qstr("Facebook")) { _photol.reset(new PhotoOpenClickHandler(_page->photo)); } else { - _photol = clickHandlerFromUrl(_page->url); + _photol = MakeShared(_page->url); } } else { - _photol = clickHandlerFromUrl(_page->url); + _photol = MakeShared(_page->url); } } else if (!_links.isEmpty()) { - _photol = clickHandlerFromUrl(_links.front().lnk->text()); + _photol = MakeShared(_links.front().lnk->text()); } if (from >= till && _page) { text = _page->description; @@ -1318,5 +1318,5 @@ void LayoutOverviewLink::getState(ClickHandlerPtr &link, HistoryCursorState &cur LayoutOverviewLink::Link::Link(const QString &url, const QString &text) : text(text) , width(st::normalFont->width(text)) -, lnk(clickHandlerFromUrl(url)) { +, lnk(MakeShared(url)) { } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 01020792cf..4bc2b95a9b 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1163,7 +1163,7 @@ bool MainWidget::addParticipantFail(UserData *user, const RPCError &error) { } else if (error.type() == "USER_ALREADY_PARTICIPANT" && user->botInfo) { text = lang(lng_bot_already_in_group); } else if (error.type() == "PEER_FLOOD") { - text = lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info))); + text = cantInviteError(); } Ui::showLayer(new InformBox(text)); return false; @@ -1181,7 +1181,7 @@ bool MainWidget::addParticipantsFail(ChannelData *channel, const RPCError &error } else if (error.type() == "USER_NOT_MUTUAL_CONTACT") { // trying to return user who does not have me in contacts text = lang(channel->isMegagroup() ? lng_failed_add_not_mutual : lng_failed_add_not_mutual_channel); } else if (error.type() == "PEER_FLOOD") { - text = lng_cant_invite_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info))); + text = cantInviteError(); } Ui::showLayer(new InformBox(text)); return false; @@ -1288,7 +1288,7 @@ bool MainWidget::sendMessageFail(const RPCError &error) { if (mtpIsFlood(error)) return false; if (error.type() == qsl("PEER_FLOOD")) { - Ui::showLayer(new InformBox(lng_cant_send_to_not_contact(lt_more_info, textcmdLink(qsl("https://telegram.org/faq?_hash=can-39t-send-messages-to-non-contacts"), lang(lng_cant_more_info))))); + Ui::showLayer(new InformBox(cantInviteError())); return true; } return false; diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 48e83e84a8..79cae75897 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -1062,8 +1062,8 @@ bool Window::eventFilter(QObject *obj, QEvent *e) { case QEvent::FileOpen: if (obj == Application::instance()) { - QString url = static_cast(e)->url().toEncoded(); - if (!url.trimmed().midRef(0, 5).compare(qsl("tg://"), Qt::CaseInsensitive)) { + QString url = static_cast(e)->url().toEncoded().trimmed(); + if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { cSetStartUrl(url); if (!cStartUrl().isEmpty() && App::main() && App::self()) { App::main()->openLocalUrl(cStartUrl());