/* 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-2017 John Preston, https://desktop.telegram.org */ #pragma once #include "base/runtime_composer.h" #include "base/flags.h" #include "base/value_ordering.h" namespace base { template class enum_mask; } // namespace base namespace Storage { enum class SharedMediaType : char; using SharedMediaTypesMask = base::enum_mask; } // namespace Storage namespace Ui { class RippleAnimation; } // namespace Ui namespace style { struct BotKeyboardButton; struct RippleAnimation; } // namespace style class HistoryElement { public: HistoryElement() = default; HistoryElement(const HistoryElement &other) = delete; HistoryElement &operator=(const HistoryElement &other) = delete; int maxWidth() const { return _maxw; } int minHeight() const { return _minh; } int height() const { return _height; } virtual ~HistoryElement() = default; protected: mutable int _maxw = 0; mutable int _minh = 0; mutable int _height = 0; }; class HistoryMessage; enum HistoryCursorState { HistoryDefaultCursorState, HistoryInTextCursorState, HistoryInDateCursorState, HistoryInForwardedCursorState, }; struct HistoryTextState { HistoryTextState() = default; HistoryTextState(not_null item); HistoryTextState( not_null item, const Text::StateResult &state); HistoryTextState( not_null item, ClickHandlerPtr link); HistoryTextState( std::nullptr_t, const Text::StateResult &state) : cursor(state.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState) , link(state.link) , afterSymbol(state.afterSymbol) , symbol(state.symbol) { } HistoryTextState(std::nullptr_t, ClickHandlerPtr link) : link(link) { } FullMsgId itemId; HistoryCursorState cursor = HistoryDefaultCursorState; ClickHandlerPtr link; bool afterSymbol = false; uint16 symbol = 0; }; struct HistoryStateRequest { Text::StateRequest::Flags flags = Text::StateRequest::Flag::LookupLink; Text::StateRequest forText() const { Text::StateRequest result; result.flags = flags; return result; } }; enum InfoDisplayType { InfoDisplayDefault, InfoDisplayOverImage, InfoDisplayOverBackground, }; struct HistoryMessageVia : public RuntimeComponent { void create(int32 userId); void resize(int32 availw) const; UserData *_bot = nullptr; mutable QString _text; mutable int _width = 0; mutable int _maxWidth = 0; ClickHandlerPtr _lnk; }; struct HistoryMessageViews : public RuntimeComponent { QString _viewsText; int _views = 0; int _viewsWidth = 0; }; struct HistoryMessageSigned : public RuntimeComponent { void create(const QString &author, const QString &date); int maxWidth() const; QString _author; Text _signature; }; struct HistoryMessageEdited : public RuntimeComponent { void refresh(const QString &date, bool displayed); int maxWidth() const; QDateTime date; Text text; }; struct HistoryMessageForwarded : public RuntimeComponent { void create(const HistoryMessageVia *via) const; QDateTime _originalDate; PeerData *_originalSender = nullptr; QString _originalAuthor; MsgId _originalId = 0; mutable Text _text = { 1 }; PeerData *_savedFromPeer = nullptr; MsgId _savedFromMsgId = 0; }; struct HistoryMessageReply : public RuntimeComponent { HistoryMessageReply() = default; HistoryMessageReply(const HistoryMessageReply &other) = delete; HistoryMessageReply(HistoryMessageReply &&other) = delete; HistoryMessageReply &operator=(const HistoryMessageReply &other) = delete; HistoryMessageReply &operator=(HistoryMessageReply &&other) { replyToMsgId = other.replyToMsgId; std::swap(replyToMsg, other.replyToMsg); replyToLnk = std::move(other.replyToLnk); replyToName = std::move(other.replyToName); replyToText = std::move(other.replyToText); replyToVersion = other.replyToVersion; _maxReplyWidth = other._maxReplyWidth; _replyToVia = std::move(other._replyToVia); return *this; } ~HistoryMessageReply() { // clearData() should be called by holder Expects(replyToMsg == nullptr); Expects(_replyToVia == nullptr); } bool updateData(HistoryMessage *holder, bool force = false); void clearData(HistoryMessage *holder); // must be called before destructor bool isNameUpdated() const; void updateName() const; void resize(int width) const; void itemRemoved(HistoryMessage *holder, HistoryItem *removed); enum class PaintFlag { InBubble = (1 << 0), Selected = (1 << 1), }; using PaintFlags = base::flags; friend inline constexpr auto is_flag_type(PaintFlag) { return true; }; void paint(Painter &p, const HistoryItem *holder, int x, int y, int w, PaintFlags flags) const; MsgId replyToId() const { return replyToMsgId; } int replyToWidth() const { return _maxReplyWidth; } ClickHandlerPtr replyToLink() const { return replyToLnk; } MsgId replyToMsgId = 0; HistoryItem *replyToMsg = nullptr; ClickHandlerPtr replyToLnk; mutable Text replyToName, replyToText; mutable int replyToVersion = 0; mutable int _maxReplyWidth = 0; std::unique_ptr _replyToVia; int toWidth = 0; }; class ReplyKeyboard; struct HistoryMessageReplyMarkup : public RuntimeComponent { HistoryMessageReplyMarkup() = default; HistoryMessageReplyMarkup(MTPDreplyKeyboardMarkup::Flags f) : flags(f) { } void create(const MTPReplyMarkup &markup); void create(const HistoryMessageReplyMarkup &markup); struct Button { enum class Type { Default, Url, Callback, RequestPhone, RequestLocation, SwitchInline, SwitchInlineSame, Game, Buy, }; Type type; QString text; QByteArray data; mutable mtpRequestId requestId; }; std::vector> rows; MTPDreplyKeyboardMarkup::Flags flags = 0; std::unique_ptr inlineKeyboard; // If >= 0 it holds the y coord of the inlineKeyboard before the last edition. int oldTop = -1; private: void createFromButtonRows(const QVector &v); }; class ReplyMarkupClickHandler : public LeftButtonClickHandler { public: ReplyMarkupClickHandler(int row, int column, FullMsgId context); QString tooltip() const override { return _fullDisplayed ? QString() : buttonText(); } void setFullDisplayed(bool full) { _fullDisplayed = full; } // Copy to clipboard support. void copyToClipboard() const override; QString copyToClipboardContextItemText() const override; // Finds the corresponding button in the items markup struct. // If the button is not found it returns nullptr. // Note: it is possible that we will point to the different button // than the one was used when constructing the handler, but not a big deal. const HistoryMessageReplyMarkup::Button *getButton() const; // We hold only FullMsgId, not HistoryItem*, because all click handlers // are activated async and the item may be already destroyed. void setMessageId(const FullMsgId &msgId) { _itemId = msgId; } protected: void onClickImpl() const override; private: FullMsgId _itemId; int _row = 0; int _column = 0; bool _fullDisplayed = true; // Returns the full text of the corresponding button. QString buttonText() const; }; class ReplyKeyboard { private: struct Button; public: class Style { public: Style(const style::BotKeyboardButton &st) : _st(&st) { } virtual void startPaint(Painter &p) const = 0; virtual const style::TextStyle &textStyle() const = 0; int buttonSkip() const; int buttonPadding() const; int buttonHeight() const; virtual int buttonRadius() const = 0; virtual void repaint(not_null item) const = 0; virtual ~Style() { } protected: virtual void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const = 0; virtual void paintButtonIcon(Painter &p, const QRect &rect, int outerWidth, HistoryMessageReplyMarkup::Button::Type type) const = 0; virtual void paintButtonLoading(Painter &p, const QRect &rect) const = 0; virtual int minButtonWidth(HistoryMessageReplyMarkup::Button::Type type) const = 0; private: const style::BotKeyboardButton *_st; void paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button, TimeMs ms) const; friend class ReplyKeyboard; }; ReplyKeyboard( not_null item, std::unique_ptr