2016-04-12 21:31:28 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
|
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
class ClickHandler;
|
|
|
|
using ClickHandlerPtr = QSharedPointer<ClickHandler>;
|
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
enum ExpandLinksMode {
|
|
|
|
ExpandLinksNone,
|
|
|
|
ExpandLinksShortened,
|
|
|
|
ExpandLinksAll,
|
2016-05-31 19:27:11 +00:00
|
|
|
ExpandLinksUrlOnly, // For custom urls leaves only url instead of text.
|
2016-04-29 12:00:48 +00:00
|
|
|
};
|
|
|
|
|
2016-04-12 21:31:28 +00:00
|
|
|
class ClickHandlerHost {
|
|
|
|
protected:
|
|
|
|
|
|
|
|
virtual void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) {
|
|
|
|
}
|
|
|
|
virtual void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) {
|
|
|
|
}
|
|
|
|
virtual ~ClickHandlerHost() = 0;
|
|
|
|
friend class ClickHandler;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
class EntityInText;
|
2016-05-06 17:33:48 +00:00
|
|
|
struct TextWithEntities;
|
2016-04-12 21:31:28 +00:00
|
|
|
class ClickHandler {
|
|
|
|
public:
|
2016-04-29 12:00:48 +00:00
|
|
|
virtual ~ClickHandler() {
|
|
|
|
}
|
|
|
|
|
2016-04-12 21:31:28 +00:00
|
|
|
virtual void onClick(Qt::MouseButton) const = 0;
|
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
// What text to show in a tooltip when mouse is over that click handler as a link in Text.
|
2016-04-12 21:31:28 +00:00
|
|
|
virtual QString tooltip() const {
|
|
|
|
return QString();
|
|
|
|
}
|
2016-04-29 12:00:48 +00:00
|
|
|
|
|
|
|
// What to drop in the input fields when dragging that click handler as a link from Text.
|
|
|
|
virtual QString dragText() const {
|
2016-04-12 21:31:28 +00:00
|
|
|
return QString();
|
|
|
|
}
|
2016-04-29 12:00:48 +00:00
|
|
|
|
|
|
|
// Copy to clipboard support.
|
|
|
|
virtual void copyToClipboard() const {
|
2016-04-12 21:31:28 +00:00
|
|
|
}
|
2016-04-29 12:00:48 +00:00
|
|
|
virtual QString copyToClipboardContextItemText() const {
|
|
|
|
return QString();
|
2016-04-12 21:31:28 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
// Entities in text support.
|
|
|
|
|
|
|
|
// This method returns empty string if just textPart should be used (nothing to expand).
|
|
|
|
virtual QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const;
|
2016-05-06 17:33:48 +00:00
|
|
|
virtual TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const;
|
2016-04-12 21:31:28 +00:00
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
// This method should be called on mouse over a click handler.
|
|
|
|
// It returns true if the active handler was changed or false otherwise.
|
2016-04-12 21:31:28 +00:00
|
|
|
static bool setActive(const ClickHandlerPtr &p, ClickHandlerHost *host = nullptr);
|
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
// This method should be called when mouse leaves the host.
|
|
|
|
// It returns true if the active handler was changed or false otherwise.
|
2016-04-12 21:31:28 +00:00
|
|
|
static bool clearActive(ClickHandlerHost *host = nullptr) {
|
|
|
|
if (host && _activeHost != host) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return setActive(ClickHandlerPtr(), host);
|
|
|
|
}
|
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
// This method should be called on mouse press event.
|
2016-04-12 21:31:28 +00:00
|
|
|
static void pressed() {
|
|
|
|
unpressed();
|
|
|
|
if (!_active || !*_active) {
|
|
|
|
return;
|
|
|
|
}
|
2016-10-12 19:34:25 +00:00
|
|
|
_pressed.createIfNull();
|
2016-04-12 21:31:28 +00:00
|
|
|
*_pressed = *_active;
|
|
|
|
if ((_pressedHost = _activeHost)) {
|
|
|
|
_pressedHost->clickHandlerPressedChanged(*_pressed, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-29 12:00:48 +00:00
|
|
|
// This method should be called on mouse release event.
|
|
|
|
// The activated click handler (if any) is returned.
|
2016-04-12 21:31:28 +00:00
|
|
|
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) {
|
2016-06-01 16:40:51 +00:00
|
|
|
if (_active) (*_active).clear();
|
2016-04-12 21:31:28 +00:00
|
|
|
_activeHost = nullptr;
|
2016-04-13 05:55:01 +00:00
|
|
|
}
|
|
|
|
if (_pressedHost == host) {
|
2016-06-01 16:40:51 +00:00
|
|
|
if (_pressed) (*_pressed).clear();
|
2016-04-12 21:31:28 +00:00
|
|
|
_pressedHost = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-06 17:33:48 +00:00
|
|
|
protected:
|
|
|
|
// For click handlers like mention or hashtag in getExpandedLinkTextWithEntities()
|
|
|
|
// we return just an empty string ("use original string part") with single entity.
|
|
|
|
TextWithEntities simpleTextWithEntity(const EntityInText &entity) const;
|
2016-04-12 21:31:28 +00:00
|
|
|
|
2016-05-06 17:33:48 +00:00
|
|
|
private:
|
2016-04-12 21:31:28 +00:00
|
|
|
static NeverFreedPointer<ClickHandlerPtr> _active;
|
|
|
|
static NeverFreedPointer<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;
|
|
|
|
|
|
|
|
};
|
2016-11-16 10:44:06 +00:00
|
|
|
|
|
|
|
class LambdaClickHandler : public ClickHandler {
|
|
|
|
public:
|
|
|
|
LambdaClickHandler(base::lambda_unique<void()> handler) : _handler(std_::move(handler)) {
|
|
|
|
}
|
|
|
|
void onClick(Qt::MouseButton button) const override final {
|
|
|
|
if (button == Qt::LeftButton && _handler) {
|
|
|
|
_handler();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
base::lambda_unique<void()> _handler;
|
|
|
|
|
|
|
|
};
|