mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-04-01 23:00:58 +00:00
Everywhere TextWithTags and TextWithEntities are used.
Copy tags from messages to clipboard, to drag mime data. Sorting entities while processing (links, monospace, mentions).
This commit is contained in:
parent
463450e607
commit
3e5f51f45a
@ -968,7 +968,9 @@ namespace {
|
||||
peerId = peerFromUser(m.vfrom_id);
|
||||
}
|
||||
if (HistoryItem *existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
|
||||
existing->setText(qs(m.vmessage), m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText());
|
||||
auto text = qs(m.vmessage);
|
||||
auto entities = m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText();
|
||||
existing->setText({ text, entities });
|
||||
existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr);
|
||||
existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
|
||||
existing->addToOverview(AddToOverviewNew);
|
||||
|
@ -414,7 +414,7 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth)
|
||||
image = doc->thumb;
|
||||
} break;
|
||||
}
|
||||
caption = media->getCaption();
|
||||
caption = media->getCaption().text;
|
||||
}
|
||||
if ((!_animated && (dimensions.isEmpty() || doc)) || image->isNull()) {
|
||||
_animated = false;
|
||||
@ -492,7 +492,8 @@ EditCaptionBox::EditCaptionBox(HistoryItem *msg) : AbstractBox(st::boxWideWidth)
|
||||
_field->setMaxLength(MaxPhotoCaption);
|
||||
_field->setCtrlEnterSubmit(CtrlEnterSubmitBoth);
|
||||
} else {
|
||||
QString text = textApplyEntities(msg->originalText(), msg->originalEntities());
|
||||
auto original = msg->originalText();
|
||||
QString text = textApplyEntities(original.text, original.entities);
|
||||
_field = new InputArea(this, st::editTextArea, lang(lng_photo_caption), text);
|
||||
// _field->setMaxLength(MaxMessageSize); // entities can make text in input field larger but still valid
|
||||
_field->setCtrlEnterSubmit(cCtrlEnter() ? CtrlEnterSubmitCtrlEnter : CtrlEnterSubmitEnter);
|
||||
|
@ -66,6 +66,12 @@ QString ClickHandler::getExpandedLinkText(ExpandLinksMode mode, const QStringRef
|
||||
return QString();
|
||||
}
|
||||
|
||||
EntityInText ClickHandler::getEntityInText(int offset, const QStringRef &textPart) const {
|
||||
return EntityInText(EntityInTextInvalid, offset, 0);
|
||||
TextWithEntities ClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||
return { QString(), EntitiesInText() };
|
||||
}
|
||||
|
||||
TextWithEntities ClickHandler::simpleTextWithEntity(const EntityInText &entity) const {
|
||||
TextWithEntities result;
|
||||
result.entities.push_back(entity);
|
||||
return result;
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ protected:
|
||||
};
|
||||
|
||||
class EntityInText;
|
||||
struct TextWithEntities;
|
||||
class ClickHandler {
|
||||
public:
|
||||
|
||||
virtual ~ClickHandler() {
|
||||
}
|
||||
|
||||
@ -71,8 +71,7 @@ public:
|
||||
|
||||
// This method returns empty string if just textPart should be used (nothing to expand).
|
||||
virtual QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const;
|
||||
|
||||
virtual EntityInText getEntityInText(int offset, const QStringRef &textPart) const;
|
||||
virtual TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const;
|
||||
|
||||
// This method should be called on mouse over a click handler.
|
||||
// It returns true if the active handler was changed or false otherwise.
|
||||
@ -152,8 +151,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
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;
|
||||
|
||||
private:
|
||||
static NeverFreedPointer<ClickHandlerPtr> _active;
|
||||
static NeverFreedPointer<ClickHandlerPtr> _pressed;
|
||||
static ClickHandlerHost *_activeHost;
|
||||
|
@ -75,18 +75,23 @@ void UrlClickHandler::doOpen(QString url) {
|
||||
}
|
||||
|
||||
QString UrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const {
|
||||
if (mode == ExpandLinksNone) {
|
||||
return QString();
|
||||
QString result;
|
||||
if (mode != ExpandLinksNone) {
|
||||
result = _originalUrl;
|
||||
}
|
||||
return _originalUrl;
|
||||
return result;
|
||||
}
|
||||
|
||||
EntityInText UrlClickHandler::getEntityInText(int offset, const QStringRef &textPart) const {
|
||||
auto u = _originalUrl;
|
||||
if (isEmail(u)) {
|
||||
return EntityInText(EntityInTextUrl, offset, u.size());
|
||||
TextWithEntities UrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||
TextWithEntities result;
|
||||
auto entityType = isEmail(_originalUrl) ? EntityInTextEmail : EntityInTextUrl;
|
||||
int entityLength = textPart.size();
|
||||
if (mode != ExpandLinksNone) {
|
||||
result.text = _originalUrl;
|
||||
entityLength = _originalUrl.size();
|
||||
}
|
||||
return EntityInText(EntityInTextUrl, offset, u.size());
|
||||
result.entities.push_back({ entityType, entityOffset, entityLength });
|
||||
return result;
|
||||
}
|
||||
|
||||
void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const {
|
||||
@ -102,14 +107,20 @@ void HiddenUrlClickHandler::onClick(Qt::MouseButton button) const {
|
||||
}
|
||||
|
||||
QString HiddenUrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const {
|
||||
if (mode != ExpandLinksAll) {
|
||||
return QString();
|
||||
QString result;
|
||||
if (mode == ExpandLinksAll) {
|
||||
result = textPart.toString() + qsl(" (") + url() + ')';
|
||||
}
|
||||
return textPart.toString() + qsl(" (") + url() + ')';
|
||||
return result;
|
||||
}
|
||||
|
||||
EntityInText HiddenUrlClickHandler::getEntityInText(int offset, const QStringRef &textPart) const {
|
||||
return EntityInText(EntityInTextCustomUrl, offset, textPart.size(), url());
|
||||
TextWithEntities HiddenUrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||
TextWithEntities result;
|
||||
result.entities.push_back({ EntityInTextCustomUrl, entityOffset, textPart.size(), url() });
|
||||
if (mode == ExpandLinksAll) {
|
||||
result.text = textPart.toString() + qsl(" (") + url() + ')';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString MentionClickHandler::copyToClipboardContextItemText() const {
|
||||
@ -122,8 +133,8 @@ void MentionClickHandler::onClick(Qt::MouseButton button) const {
|
||||
}
|
||||
}
|
||||
|
||||
EntityInText MentionClickHandler::getEntityInText(int offset, const QStringRef &textPart) const {
|
||||
return EntityInText(EntityInTextMention, offset, textPart.size());
|
||||
TextWithEntities MentionClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||
return simpleTextWithEntity({ EntityInTextMention, entityOffset, textPart.size() });
|
||||
}
|
||||
|
||||
void MentionNameClickHandler::onClick(Qt::MouseButton button) const {
|
||||
@ -134,9 +145,9 @@ void MentionNameClickHandler::onClick(Qt::MouseButton button) const {
|
||||
}
|
||||
}
|
||||
|
||||
EntityInText MentionNameClickHandler::getEntityInText(int offset, const QStringRef &textPart) const {
|
||||
TextWithEntities MentionNameClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||
auto data = QString::number(_userId) + '.' + QString::number(_accessHash);
|
||||
return EntityInText(EntityInTextMentionName, offset, textPart.size(), data);
|
||||
return simpleTextWithEntity({ EntityInTextMentionName, entityOffset, textPart.size(), data });
|
||||
}
|
||||
|
||||
QString MentionNameClickHandler::tooltip() const {
|
||||
@ -159,8 +170,8 @@ void HashtagClickHandler::onClick(Qt::MouseButton button) const {
|
||||
}
|
||||
}
|
||||
|
||||
EntityInText HashtagClickHandler::getEntityInText(int offset, const QStringRef &textPart) const {
|
||||
return EntityInText(EntityInTextHashtag, offset, textPart.size());
|
||||
TextWithEntities HashtagClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
|
||||
}
|
||||
|
||||
void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
|
||||
@ -180,6 +191,6 @@ void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
|
||||
}
|
||||
}
|
||||
|
||||
EntityInText BotCommandClickHandler::getEntityInText(int offset, const QStringRef &textPart) const {
|
||||
return EntityInText(EntityInTextHashtag, offset, textPart.size());
|
||||
TextWithEntities BotCommandClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ public:
|
||||
}
|
||||
|
||||
QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const override;
|
||||
EntityInText getEntityInText(int offset, const QStringRef &textPart) const override;
|
||||
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
||||
|
||||
static void doOpen(QString url);
|
||||
void onClick(Qt::MouseButton button) const override {
|
||||
@ -118,7 +118,7 @@ public:
|
||||
void onClick(Qt::MouseButton button) const override;
|
||||
|
||||
QString getExpandedLinkText(ExpandLinksMode mode, const QStringRef &textPart) const override;
|
||||
EntityInText getEntityInText(int offset, const QStringRef &textPart) const override;
|
||||
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
||||
|
||||
};
|
||||
|
||||
@ -135,7 +135,7 @@ public:
|
||||
|
||||
QString copyToClipboardContextItemText() const override;
|
||||
|
||||
EntityInText getEntityInText(int offset, const QStringRef &textPart) const override;
|
||||
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
||||
|
||||
protected:
|
||||
QString url() const override {
|
||||
@ -157,7 +157,7 @@ public:
|
||||
|
||||
void onClick(Qt::MouseButton button) const override;
|
||||
|
||||
EntityInText getEntityInText(int offset, const QStringRef &textPart) const override;
|
||||
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
||||
|
||||
QString tooltip() const override;
|
||||
|
||||
@ -181,7 +181,7 @@ public:
|
||||
|
||||
QString copyToClipboardContextItemText() const override;
|
||||
|
||||
EntityInText getEntityInText(int offset, const QStringRef &textPart) const override;
|
||||
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
||||
|
||||
protected:
|
||||
QString url() const override {
|
||||
@ -204,7 +204,7 @@ public:
|
||||
return _cmd;
|
||||
}
|
||||
|
||||
EntityInText getEntityInText(int offset, const QStringRef &textPart) const override;
|
||||
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
||||
|
||||
protected:
|
||||
QString url() const override {
|
||||
|
@ -1032,7 +1032,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||
EntitiesInText entities;
|
||||
textParseEntities(text, _historyTextNoMonoOptions.flags, &entities);
|
||||
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);
|
||||
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, m.has_from_id() ? m.vfrom_id.v : 0);
|
||||
} else {
|
||||
@ -3095,22 +3095,21 @@ void HistoryItem::setId(MsgId newId) {
|
||||
}
|
||||
|
||||
bool HistoryItem::canEdit(const QDateTime &cur) const {
|
||||
int32 s = date.secsTo(cur);
|
||||
auto channel = _history->peer->asChannel();
|
||||
if (!channel || id < 0 || date.secsTo(cur) >= Global::EditTimeLimit()) return false;
|
||||
|
||||
if (const HistoryMessage *msg = toHistoryMessage()) {
|
||||
if (auto msg = toHistoryMessage()) {
|
||||
if (msg->Has<HistoryMessageVia>() || msg->Has<HistoryMessageForwarded>()) return false;
|
||||
|
||||
if (HistoryMedia *media = msg->getMedia()) {
|
||||
HistoryMediaType t = media->type();
|
||||
if (t != MediaTypePhoto &&
|
||||
t != MediaTypeVideo &&
|
||||
t != MediaTypeFile &&
|
||||
t != MediaTypeGif &&
|
||||
t != MediaTypeMusicFile &&
|
||||
t != MediaTypeVoiceFile &&
|
||||
t != MediaTypeWebPage) {
|
||||
auto type = media->type();
|
||||
if (type != MediaTypePhoto &&
|
||||
type != MediaTypeVideo &&
|
||||
type != MediaTypeFile &&
|
||||
type != MediaTypeGif &&
|
||||
type != MediaTypeMusicFile &&
|
||||
type != MediaTypeVoiceFile &&
|
||||
type != MediaTypeWebPage) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -3304,18 +3303,24 @@ int32 gifMaxStatusWidth(DocumentData *document) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QString captionedSelectedText(const QString &attachType, const Text &caption, TextSelection selection) {
|
||||
TextWithEntities captionedSelectedText(const QString &attachType, const Text &caption, TextSelection selection) {
|
||||
if (selection != FullSelection) {
|
||||
return caption.original(selection, ExpandLinksAll);
|
||||
return caption.originalTextWithEntities(selection, ExpandLinksAll);
|
||||
}
|
||||
QString result;
|
||||
result.reserve(5 + attachType.size() + caption.length());
|
||||
result.append(qstr("[ ")).append(attachType).append(qstr(" ]"));
|
||||
|
||||
TextWithEntities result, original;
|
||||
if (!caption.isEmpty()) {
|
||||
result.append(qstr("\n")).append(caption.original(AllTextSelection));
|
||||
original = caption.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
|
||||
}
|
||||
result.text.reserve(5 + attachType.size() + original.text.size());
|
||||
result.text.append(qstr("[ ")).append(attachType).append(qstr(" ]"));
|
||||
if (!caption.isEmpty()) {
|
||||
result.text.append(qstr("\n"));
|
||||
appendTextWithEntities(result, std_::move(original));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
@ -3735,10 +3740,10 @@ void HistoryPhoto::detachFromParent() {
|
||||
}
|
||||
|
||||
QString HistoryPhoto::inDialogsText() const {
|
||||
return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.original(AllTextSelection, ExpandLinksNone);
|
||||
return _caption.isEmpty() ? lang(lng_in_dlg_photo) : _caption.originalText(AllTextSelection, ExpandLinksNone);
|
||||
}
|
||||
|
||||
QString HistoryPhoto::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistoryPhoto::selectedText(TextSelection selection) const {
|
||||
return captionedSelectedText(lang(lng_in_dlg_photo), _caption, selection);
|
||||
}
|
||||
|
||||
@ -3981,10 +3986,10 @@ void HistoryVideo::setStatusSize(int32 newSize) const {
|
||||
}
|
||||
|
||||
QString HistoryVideo::inDialogsText() const {
|
||||
return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.original(AllTextSelection, ExpandLinksNone);
|
||||
return _caption.isEmpty() ? lang(lng_in_dlg_video) : _caption.originalText(AllTextSelection, ExpandLinksNone);
|
||||
}
|
||||
|
||||
QString HistoryVideo::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistoryVideo::selectedText(TextSelection selection) const {
|
||||
return captionedSelectedText(lang(lng_in_dlg_video), _caption, selection);
|
||||
}
|
||||
|
||||
@ -4472,13 +4477,13 @@ QString HistoryDocument::inDialogsText() const {
|
||||
}
|
||||
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
if (!captioned->_caption.isEmpty()) {
|
||||
result.append(' ').append(captioned->_caption.original(AllTextSelection, ExpandLinksNone));
|
||||
result.append(' ').append(captioned->_caption.originalText(AllTextSelection, ExpandLinksNone));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString HistoryDocument::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistoryDocument::selectedText(TextSelection selection) const {
|
||||
const Text emptyCaption;
|
||||
const Text *caption = &emptyCaption;
|
||||
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
@ -4930,10 +4935,10 @@ HistoryTextState HistoryGif::getState(int x, int y, HistoryStateRequest request)
|
||||
}
|
||||
|
||||
QString HistoryGif::inDialogsText() const {
|
||||
return qsl("GIF") + (_caption.isEmpty() ? QString() : (' ' + _caption.original(AllTextSelection, ExpandLinksNone)));
|
||||
return qsl("GIF") + (_caption.isEmpty() ? QString() : (' ' + _caption.originalText(AllTextSelection, ExpandLinksNone)));
|
||||
}
|
||||
|
||||
QString HistoryGif::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistoryGif::selectedText(TextSelection selection) const {
|
||||
return captionedSelectedText(qsl("GIF"), _caption, selection);
|
||||
}
|
||||
|
||||
@ -5250,11 +5255,11 @@ QString HistorySticker::inDialogsText() const {
|
||||
return _emoji.isEmpty() ? lang(lng_in_dlg_sticker) : lng_in_dlg_sticker_emoji(lt_emoji, _emoji);
|
||||
}
|
||||
|
||||
QString HistorySticker::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistorySticker::selectedText(TextSelection selection) const {
|
||||
if (selection != FullSelection) {
|
||||
return QString();
|
||||
return TextWithEntities();
|
||||
}
|
||||
return qsl("[ ") + inDialogsText() + qsl(" ]");
|
||||
return { qsl("[ ") + inDialogsText() + qsl(" ]"), EntitiesInText() };
|
||||
}
|
||||
|
||||
void HistorySticker::attachToParent() {
|
||||
@ -5435,11 +5440,11 @@ QString HistoryContact::inDialogsText() const {
|
||||
return lang(lng_in_dlg_contact);
|
||||
}
|
||||
|
||||
QString HistoryContact::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistoryContact::selectedText(TextSelection selection) const {
|
||||
if (selection != FullSelection) {
|
||||
return QString();
|
||||
return TextWithEntities();
|
||||
}
|
||||
return qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" ]\n") + _name.original() + '\n' + _phone;
|
||||
return { qsl("[ ") + lang(lng_in_dlg_contact) + qsl(" ]\n") + _name.originalText() + '\n' + _phone, EntitiesInText() };
|
||||
}
|
||||
|
||||
void HistoryContact::attachToParent() {
|
||||
@ -5956,18 +5961,21 @@ QString HistoryWebPage::inDialogsText() const {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString HistoryWebPage::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistoryWebPage::selectedText(TextSelection selection) const {
|
||||
if (selection == FullSelection) {
|
||||
return QString();
|
||||
return TextWithEntities();
|
||||
}
|
||||
auto titleResult = _title.original(selection, ExpandLinksAll);
|
||||
auto descriptionResult = _description.original(toDescriptionSelection(selection), ExpandLinksAll);
|
||||
if (titleResult.isEmpty()) {
|
||||
auto titleResult = _title.originalTextWithEntities(selection, ExpandLinksAll);
|
||||
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection), ExpandLinksAll);
|
||||
if (titleResult.text.isEmpty()) {
|
||||
return descriptionResult;
|
||||
} else if (descriptionResult.isEmpty()) {
|
||||
} else if (descriptionResult.text.isEmpty()) {
|
||||
return titleResult;
|
||||
}
|
||||
return titleResult + '\n' + descriptionResult;
|
||||
|
||||
titleResult.text += '\n';
|
||||
appendTextWithEntities(titleResult, std_::move(descriptionResult));
|
||||
return titleResult;
|
||||
}
|
||||
|
||||
ImagePtr HistoryWebPage::replyPreview() {
|
||||
@ -6399,24 +6407,31 @@ TextSelection HistoryLocation::adjustSelection(TextSelection selection, TextSele
|
||||
}
|
||||
|
||||
QString HistoryLocation::inDialogsText() const {
|
||||
return _title.isEmpty() ? lang(lng_maps_point) : _title.original(AllTextSelection);
|
||||
return _title.isEmpty() ? lang(lng_maps_point) : _title.originalText(AllTextSelection);
|
||||
}
|
||||
|
||||
QString HistoryLocation::selectedText(TextSelection selection) const {
|
||||
TextWithEntities HistoryLocation::selectedText(TextSelection selection) const {
|
||||
if (selection == FullSelection) {
|
||||
auto result = qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n");
|
||||
TextWithEntities result = { qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"), EntitiesInText() };
|
||||
auto info = selectedText(AllTextSelection);
|
||||
if (!info.isEmpty()) result.append(info).append('\n');
|
||||
return result + _link->dragText();
|
||||
if (!info.text.isEmpty()) {
|
||||
appendTextWithEntities(result, std_::move(info));
|
||||
result.text.append('\n');
|
||||
}
|
||||
result.text += _link->dragText();
|
||||
return result;
|
||||
}
|
||||
auto titleResult = _title.original(selection);
|
||||
auto descriptionResult = _description.original(toDescriptionSelection(selection));
|
||||
if (titleResult.isEmpty()) {
|
||||
|
||||
auto titleResult = _title.originalTextWithEntities(selection);
|
||||
auto descriptionResult = _description.originalTextWithEntities(toDescriptionSelection(selection));
|
||||
if (titleResult.text.isEmpty()) {
|
||||
return descriptionResult;
|
||||
} else if (descriptionResult.isEmpty()) {
|
||||
} else if (descriptionResult.text.isEmpty()) {
|
||||
return titleResult;
|
||||
}
|
||||
return titleResult + '\n' + descriptionResult;
|
||||
titleResult.text += '\n';
|
||||
appendTextWithEntities(titleResult, std_::move(descriptionResult));
|
||||
return titleResult;
|
||||
}
|
||||
|
||||
int32 HistoryLocation::fullWidth() const {
|
||||
@ -6729,7 +6744,12 @@ HistoryMessage::HistoryMessage(History *history, const MTPDmessage &msg)
|
||||
|
||||
QString text(textClean(qs(msg.vmessage)));
|
||||
initMedia(msg.has_media() ? (&msg.vmedia) : nullptr, text);
|
||||
setText(text, msg.has_entities() ? entitiesFromMTP(msg.ventities.c_vector().v) : EntitiesInText());
|
||||
|
||||
TextWithEntities textWithEntities = { text, EntitiesInText() };
|
||||
if (msg.has_entities()) {
|
||||
textWithEntities.entities = entitiesFromMTP(msg.ventities.c_vector().v);
|
||||
}
|
||||
setText(textWithEntities);
|
||||
}
|
||||
|
||||
HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd)
|
||||
@ -6755,14 +6775,14 @@ HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags fl
|
||||
if (HistoryMedia *mediaOriginal = fwd->getMedia()) {
|
||||
_media.reset(mediaOriginal->clone(this));
|
||||
}
|
||||
setText(fwd->originalText(), fwd->originalEntities());
|
||||
setText(fwd->originalText());
|
||||
}
|
||||
|
||||
HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities)
|
||||
HistoryMessage::HistoryMessage(History *history, MsgId id, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities)
|
||||
: HistoryItem(history, id, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
|
||||
createComponentsHelper(flags, replyTo, viaBotId, MTPnullMarkup);
|
||||
|
||||
setText(msg, entities);
|
||||
setText(textWithEntities);
|
||||
}
|
||||
|
||||
HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup)
|
||||
@ -6770,7 +6790,7 @@ HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags
|
||||
createComponentsHelper(flags, replyTo, viaBotId, markup);
|
||||
|
||||
initMediaFromDocument(doc, caption);
|
||||
setText(QString(), EntitiesInText());
|
||||
setText(TextWithEntities());
|
||||
}
|
||||
|
||||
HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup)
|
||||
@ -6778,7 +6798,7 @@ HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags
|
||||
createComponentsHelper(flags, replyTo, viaBotId, markup);
|
||||
|
||||
_media.reset(new HistoryPhoto(this, photo, caption));
|
||||
setText(QString(), EntitiesInText());
|
||||
setText(TextWithEntities());
|
||||
}
|
||||
|
||||
void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, const MTPReplyMarkup &markup) {
|
||||
@ -7069,11 +7089,6 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
|
||||
}
|
||||
}
|
||||
|
||||
EntitiesInText entities;
|
||||
if (message.has_entities()) {
|
||||
entities = entitiesFromMTP(message.ventities.c_vector().v);
|
||||
}
|
||||
|
||||
if (message.has_edit_date()) {
|
||||
_flags |= MTPDmessage::Flag::f_edit_date;
|
||||
if (!Has<HistoryMessageEdited>()) {
|
||||
@ -7083,7 +7098,11 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
|
||||
initTime();
|
||||
}
|
||||
|
||||
setText(qs(message.vmessage), entities);
|
||||
TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() };
|
||||
if (message.has_entities()) {
|
||||
textWithEntities.entities = entitiesFromMTP(message.ventities.c_vector().v);
|
||||
}
|
||||
setText(textWithEntities);
|
||||
setMedia(message.has_media() ? (&message.vmedia) : nullptr);
|
||||
setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr);
|
||||
setViewsCount(message.has_views() ? message.vviews.v : -1);
|
||||
@ -7157,36 +7176,44 @@ void HistoryMessage::eraseFromOverview() {
|
||||
}
|
||||
}
|
||||
|
||||
QString HistoryMessage::selectedText(TextSelection selection) const {
|
||||
QString result, textResult, mediaResult;
|
||||
TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
|
||||
TextWithEntities result, textResult, mediaResult;
|
||||
if (selection == FullSelection) {
|
||||
textResult = _text.original(AllTextSelection, ExpandLinksAll);
|
||||
textResult = _text.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
|
||||
} else {
|
||||
textResult = _text.original(selection, ExpandLinksAll);
|
||||
textResult = _text.originalTextWithEntities(selection, ExpandLinksAll);
|
||||
}
|
||||
if (_media) {
|
||||
mediaResult = _media->selectedText(toMediaSelection(selection));
|
||||
}
|
||||
if (textResult.isEmpty()) {
|
||||
if (textResult.text.isEmpty()) {
|
||||
result = mediaResult;
|
||||
} else if (mediaResult.isEmpty()) {
|
||||
} else if (mediaResult.text.isEmpty()) {
|
||||
result = textResult;
|
||||
} else {
|
||||
result = textResult + qstr("\n\n") + mediaResult;
|
||||
result.text = textResult.text + qstr("\n\n");
|
||||
result.entities = textResult.entities;
|
||||
appendTextWithEntities(result, std_::move(mediaResult));
|
||||
}
|
||||
if (auto fwd = Get<HistoryMessageForwarded>()) {
|
||||
if (selection == FullSelection) {
|
||||
QString fwdinfo = fwd->_text.original(AllTextSelection, ExpandLinksAll), wrapped;
|
||||
wrapped.reserve(fwdinfo.size() + 4 + result.size());
|
||||
wrapped.append('[').append(fwdinfo).append(qsl("]\n")).append(result);
|
||||
auto fwdinfo = fwd->_text.originalTextWithEntities(AllTextSelection, ExpandLinksAll);
|
||||
TextWithEntities wrapped;
|
||||
wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size());
|
||||
wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size());
|
||||
wrapped.text.append('[');
|
||||
appendTextWithEntities(wrapped, std_::move(fwdinfo));
|
||||
wrapped.text.append(qsl("]\n"));
|
||||
appendTextWithEntities(wrapped, std_::move(result));
|
||||
result = wrapped;
|
||||
}
|
||||
}
|
||||
if (auto reply = Get<HistoryMessageReply>()) {
|
||||
if (selection == FullSelection && reply->replyToMsg) {
|
||||
QString wrapped;
|
||||
wrapped.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.size());
|
||||
wrapped.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n")).append(result);
|
||||
TextWithEntities wrapped;
|
||||
wrapped.text.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.text.size());
|
||||
wrapped.text.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n"));
|
||||
appendTextWithEntities(wrapped, std_::move(result));
|
||||
result = wrapped;
|
||||
}
|
||||
}
|
||||
@ -7194,7 +7221,7 @@ QString HistoryMessage::selectedText(TextSelection selection) const {
|
||||
}
|
||||
|
||||
QString HistoryMessage::inDialogsText() const {
|
||||
return emptyText() ? (_media ? _media->inDialogsText() : QString()) : _text.original(AllTextSelection, ExpandLinksNone);
|
||||
return emptyText() ? (_media ? _media->inDialogsText() : QString()) : _text.originalText(AllTextSelection, ExpandLinksNone);
|
||||
}
|
||||
|
||||
HistoryMedia *HistoryMessage::getMedia() const {
|
||||
@ -7222,16 +7249,16 @@ void HistoryMessage::setMedia(const MTPMessageMedia *media) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMessage::setText(const QString &text, const EntitiesInText &entities) {
|
||||
void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
||||
textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
|
||||
if (_media && _media->isDisplayed()) {
|
||||
_text.setMarkedText(st::msgFont, text, entities, itemTextOptions(this));
|
||||
_text.setMarkedText(st::msgFont, textWithEntities, itemTextOptions(this));
|
||||
} else {
|
||||
_text.setMarkedText(st::msgFont, text + skipBlock(), entities, itemTextOptions(this));
|
||||
_text.setMarkedText(st::msgFont, { textWithEntities.text + skipBlock(), textWithEntities.entities }, itemTextOptions(this));
|
||||
}
|
||||
textstyleRestore();
|
||||
|
||||
for_const (const auto &entity, entities) {
|
||||
for_const (auto &entity, textWithEntities.entities) {
|
||||
auto type = entity.type();
|
||||
if (type == EntityInTextUrl || type == EntityInTextCustomUrl || type == EntityInTextEmail) {
|
||||
_flags |= MTPDmessage_ClientFlag::f_has_text_links;
|
||||
@ -7286,15 +7313,14 @@ void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
|
||||
}
|
||||
}
|
||||
|
||||
QString HistoryMessage::originalText() const {
|
||||
return emptyText() ? QString() : _text.original();
|
||||
TextWithEntities HistoryMessage::originalText() const {
|
||||
if (emptyText()) {
|
||||
return { QString(), EntitiesInText() };
|
||||
}
|
||||
return _text.originalTextWithEntities();
|
||||
}
|
||||
|
||||
EntitiesInText HistoryMessage::originalEntities() const {
|
||||
return emptyText() ? EntitiesInText() : _text.originalEntities();
|
||||
}
|
||||
|
||||
bool HistoryMessage::textHasLinks() {
|
||||
bool HistoryMessage::textHasLinks() const {
|
||||
return emptyText() ? false : _text.hasLinks();
|
||||
}
|
||||
|
||||
@ -8114,7 +8140,7 @@ bool HistoryService::updatePinnedText(const QString *pfrom, QString *ptext) {
|
||||
case MediaTypeVoiceFile: mediaText = lang(lng_action_pinned_media_voice); break;
|
||||
}
|
||||
if (mediaText.isEmpty()) {
|
||||
QString original = pinned->msg->originalText();
|
||||
QString original = pinned->msg->originalText().text;
|
||||
int32 cutat = 0, limit = PinnedMessageTextLimit, size = original.size();
|
||||
for (; limit > 0;) {
|
||||
--limit;
|
||||
@ -8192,12 +8218,12 @@ void HistoryService::countPositionAndSize(int32 &left, int32 &width) const {
|
||||
width = maxwidth - st::msgServiceMargin.left() - st::msgServiceMargin.left();
|
||||
}
|
||||
|
||||
QString HistoryService::selectedText(TextSelection selection) const {
|
||||
return _text.original((selection == FullSelection) ? AllTextSelection : selection);
|
||||
TextWithEntities HistoryService::selectedText(TextSelection selection) const {
|
||||
return _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection);
|
||||
}
|
||||
|
||||
QString HistoryService::inDialogsText() const {
|
||||
return _text.original(AllTextSelection, ExpandLinksNone);
|
||||
return _text.originalText(AllTextSelection, ExpandLinksNone);
|
||||
}
|
||||
|
||||
QString HistoryService::inReplyText() const {
|
||||
@ -8381,7 +8407,7 @@ void HistoryService::drawInDialog(Painter &p, const QRect &r, bool act, const Hi
|
||||
}
|
||||
|
||||
QString HistoryService::notificationText() const {
|
||||
QString msg = _text.original();
|
||||
QString msg = _text.originalText();
|
||||
if (msg.size() > 0xFF) msg = msg.mid(0, 0xFF) + qsl("...");
|
||||
return msg;
|
||||
}
|
||||
|
@ -1293,8 +1293,8 @@ public:
|
||||
}
|
||||
virtual void previousItemChanged();
|
||||
|
||||
virtual QString selectedText(TextSelection selection) const {
|
||||
return qsl("[-]");
|
||||
virtual TextWithEntities selectedText(TextSelection selection) const {
|
||||
return { qsl("[-]"), EntitiesInText() };
|
||||
}
|
||||
virtual QString inDialogsText() const {
|
||||
return qsl("-");
|
||||
@ -1302,6 +1302,9 @@ public:
|
||||
virtual QString inReplyText() const {
|
||||
return inDialogsText();
|
||||
}
|
||||
virtual TextWithEntities originalText() const {
|
||||
return { QString(), EntitiesInText() };
|
||||
}
|
||||
|
||||
virtual void drawInfo(Painter &p, int32 right, int32 bottom, int32 width, bool selected, InfoDisplayType type) const {
|
||||
}
|
||||
@ -1363,15 +1366,9 @@ public:
|
||||
virtual HistoryMedia *getMedia() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual void setText(const QString &text, const EntitiesInText &links) {
|
||||
virtual void setText(const TextWithEntities &textWithEntities) {
|
||||
}
|
||||
virtual QString originalText() const {
|
||||
return QString();
|
||||
}
|
||||
virtual EntitiesInText originalEntities() const {
|
||||
return EntitiesInText();
|
||||
}
|
||||
virtual bool textHasLinks() {
|
||||
virtual bool textHasLinks() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1663,7 +1660,7 @@ public:
|
||||
|
||||
virtual HistoryMediaType type() const = 0;
|
||||
virtual QString inDialogsText() const = 0;
|
||||
virtual QString selectedText(TextSelection selection) const = 0;
|
||||
virtual TextWithEntities selectedText(TextSelection selection) const = 0;
|
||||
|
||||
bool hasPoint(int x, int y) const {
|
||||
return (x >= 0 && y >= 0 && x < _width && y < _height);
|
||||
@ -1750,8 +1747,8 @@ public:
|
||||
virtual ImagePtr replyPreview() {
|
||||
return ImagePtr();
|
||||
}
|
||||
virtual QString getCaption() const {
|
||||
return QString();
|
||||
virtual TextWithEntities getCaption() const {
|
||||
return TextWithEntities();
|
||||
}
|
||||
virtual bool needsBubble() const = 0;
|
||||
virtual bool customInfoLayout() const = 0;
|
||||
@ -1900,7 +1897,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
PhotoData *photo() const {
|
||||
return _data;
|
||||
@ -1917,8 +1914,8 @@ public:
|
||||
}
|
||||
ImagePtr replyPreview() override;
|
||||
|
||||
QString getCaption() const override {
|
||||
return _caption.original();
|
||||
TextWithEntities getCaption() const override {
|
||||
return _caption.originalTextWithEntities();
|
||||
}
|
||||
bool needsBubble() const override {
|
||||
if (!_caption.isEmpty()) {
|
||||
@ -1980,7 +1977,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
DocumentData *getDocument() override {
|
||||
return _data;
|
||||
@ -2000,8 +1997,8 @@ public:
|
||||
}
|
||||
ImagePtr replyPreview() override;
|
||||
|
||||
QString getCaption() const override {
|
||||
return _caption.original();
|
||||
TextWithEntities getCaption() const override {
|
||||
return _caption.originalTextWithEntities();
|
||||
}
|
||||
bool needsBubble() const override {
|
||||
if (!_caption.isEmpty()) {
|
||||
@ -2103,7 +2100,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
bool uploading() const override {
|
||||
return _data->uploading();
|
||||
@ -2124,11 +2121,11 @@ public:
|
||||
}
|
||||
ImagePtr replyPreview() override;
|
||||
|
||||
QString getCaption() const override {
|
||||
TextWithEntities getCaption() const override {
|
||||
if (const HistoryDocumentCaptioned *captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
return captioned->_caption.original();
|
||||
return captioned->_caption.originalTextWithEntities();
|
||||
}
|
||||
return QString();
|
||||
return TextWithEntities();
|
||||
}
|
||||
bool needsBubble() const override {
|
||||
return true;
|
||||
@ -2190,7 +2187,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
bool uploading() const override {
|
||||
return _data->uploading();
|
||||
@ -2217,8 +2214,8 @@ public:
|
||||
}
|
||||
ImagePtr replyPreview() override;
|
||||
|
||||
QString getCaption() const override {
|
||||
return _caption.original();
|
||||
TextWithEntities getCaption() const override {
|
||||
return _caption.originalTextWithEntities();
|
||||
}
|
||||
bool needsBubble() const override {
|
||||
if (!_caption.isEmpty()) {
|
||||
@ -2288,7 +2285,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
DocumentData *getDocument() override {
|
||||
return _data;
|
||||
@ -2357,7 +2354,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
void attachToParent() override;
|
||||
void detachFromParent() override;
|
||||
@ -2425,7 +2422,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
@ -2559,7 +2556,7 @@ public:
|
||||
}
|
||||
|
||||
QString inDialogsText() const override;
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
bool needsBubble() const override {
|
||||
if (!_title.isEmpty() || !_description.isEmpty()) {
|
||||
@ -2613,8 +2610,8 @@ public:
|
||||
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd) {
|
||||
return _create(history, msgId, flags, date, from, fwd);
|
||||
}
|
||||
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities) {
|
||||
return _create(history, msgId, flags, replyTo, viaBotId, date, from, msg, entities);
|
||||
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities) {
|
||||
return _create(history, msgId, flags, replyTo, viaBotId, date, from, textWithEntities);
|
||||
}
|
||||
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup) {
|
||||
return _create(history, msgId, flags, replyTo, viaBotId, date, from, doc, caption, markup);
|
||||
@ -2685,13 +2682,12 @@ public:
|
||||
int32 addToOverview(AddToOverviewMethod method) override;
|
||||
void eraseFromOverview();
|
||||
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
QString inDialogsText() const override;
|
||||
HistoryMedia *getMedia() const override;
|
||||
void setText(const QString &text, const EntitiesInText &entities) override;
|
||||
QString originalText() const override;
|
||||
EntitiesInText originalEntities() const override;
|
||||
bool textHasLinks() override;
|
||||
void setText(const TextWithEntities &textWithEntities) override;
|
||||
TextWithEntities originalText() const override;
|
||||
bool textHasLinks() const override;
|
||||
|
||||
int32 infoWidth() const override {
|
||||
int32 result = _timeWidth;
|
||||
@ -2753,7 +2749,7 @@ private:
|
||||
|
||||
HistoryMessage(History *history, const MTPDmessage &msg);
|
||||
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *fwd); // local forwarded
|
||||
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const QString &msg, const EntitiesInText &entities); // local message
|
||||
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities); // local message
|
||||
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); // local document
|
||||
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); // local photo
|
||||
friend class HistoryItemInstantiated<HistoryMessage>;
|
||||
@ -2902,7 +2898,7 @@ public:
|
||||
bool serviceMsg() const override {
|
||||
return true;
|
||||
}
|
||||
QString selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
QString inDialogsText() const override;
|
||||
QString inReplyText() const override;
|
||||
|
||||
@ -2939,8 +2935,8 @@ public:
|
||||
|
||||
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
|
||||
|
||||
QString selectedText(TextSelection selection) const override {
|
||||
return QString();
|
||||
TextWithEntities selectedText(TextSelection selection) const override {
|
||||
return { QString(), EntitiesInText() };
|
||||
}
|
||||
HistoryItemType type() const override {
|
||||
return HistoryItemGroup;
|
||||
@ -2989,8 +2985,8 @@ public:
|
||||
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
|
||||
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
|
||||
|
||||
QString selectedText(TextSelection selection) const override {
|
||||
return QString();
|
||||
TextWithEntities selectedText(TextSelection selection) const override {
|
||||
return { QString(), EntitiesInText() };
|
||||
}
|
||||
HistoryItemType type() const override {
|
||||
return HistoryItemCollapse;
|
||||
|
@ -39,6 +39,54 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#include "window/top_bar_widget.h"
|
||||
#include "playerwidget.h"
|
||||
|
||||
namespace {
|
||||
|
||||
QString mimeTagFromTag(const QString &tagId) {
|
||||
if (tagId.startsWith(qstr("mention://"))) {
|
||||
return tagId + ':' + QString::number(MTP::authedId());
|
||||
}
|
||||
return tagId;
|
||||
}
|
||||
|
||||
QMimeData *mimeDataFromTextWithEntities(const TextWithEntities &forClipboard) {
|
||||
if (forClipboard.text.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto result = new QMimeData();
|
||||
result->setText(forClipboard.text);
|
||||
auto tags = textTagsFromEntities(forClipboard.entities);
|
||||
if (!tags.isEmpty()) {
|
||||
for (auto &tag : tags) {
|
||||
tag.id = mimeTagFromTag(tag.id);
|
||||
}
|
||||
result->setData(FlatTextarea::tagsMimeType(), FlatTextarea::serializeTagsList(tags));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// For mention tags save and validate userId, ignore tags for different userId.
|
||||
class FieldTagMimeProcessor : public FlatTextarea::TagMimeProcessor {
|
||||
public:
|
||||
QString mimeTagFromTag(const QString &tagId) override {
|
||||
return ::mimeTagFromTag(tagId);
|
||||
}
|
||||
|
||||
QString tagFromMimeTag(const QString &mimeTag) override {
|
||||
if (mimeTag.startsWith(qstr("mention://"))) {
|
||||
auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
|
||||
if (!match.hasMatch() || match.capturedRef(1).toInt() != MTP::authedId()) {
|
||||
return QString();
|
||||
}
|
||||
return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
|
||||
}
|
||||
return mimeTag;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
|
||||
|
||||
HistoryInner::HistoryInner(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : TWidget(nullptr)
|
||||
@ -704,24 +752,21 @@ void HistoryInner::onDragExec() {
|
||||
}
|
||||
}
|
||||
ClickHandlerPtr pressedHandler = ClickHandler::getPressed();
|
||||
QString sel;
|
||||
TextWithEntities sel;
|
||||
QList<QUrl> urls;
|
||||
if (uponSelected) {
|
||||
sel = getSelectedText();
|
||||
} else if (pressedHandler) {
|
||||
sel = pressedHandler->dragText();
|
||||
sel = { pressedHandler->dragText(), EntitiesInText() };
|
||||
//if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') {
|
||||
// urls.push_back(QUrl::fromEncoded(sel.toUtf8())); // Google Chrome crashes in Mac OS X O_o
|
||||
//}
|
||||
}
|
||||
if (!sel.isEmpty()) {
|
||||
if (auto mimeData = mimeDataFromTextWithEntities(sel)) {
|
||||
updateDragSelection(0, 0, false);
|
||||
_widget->noSelectingScroll();
|
||||
|
||||
QDrag *drag = new QDrag(App::wnd());
|
||||
QMimeData *mimeData = new QMimeData;
|
||||
|
||||
mimeData->setText(sel);
|
||||
if (!urls.isEmpty()) mimeData->setUrls(urls);
|
||||
if (uponSelected && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && !Adaptive::OneColumn()) {
|
||||
mimeData->setData(qsl("application/x-td-forward-selected"), "1");
|
||||
@ -748,7 +793,7 @@ void HistoryInner::onDragExec() {
|
||||
}
|
||||
if (!forwardMimeType.isEmpty()) {
|
||||
QDrag *drag = new QDrag(App::wnd());
|
||||
QMimeData *mimeData = new QMimeData;
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
|
||||
mimeData->setData(forwardMimeType, "1");
|
||||
if (DocumentData *document = (pressedMedia ? pressedMedia->getDocument() : nullptr)) {
|
||||
@ -1124,10 +1169,7 @@ void HistoryInner::onMenuDestroy(QObject *obj) {
|
||||
}
|
||||
|
||||
void HistoryInner::copySelectedText() {
|
||||
QString sel = getSelectedText();
|
||||
if (!sel.isEmpty()) {
|
||||
QApplication::clipboard()->setText(sel);
|
||||
}
|
||||
setToClipboard(getSelectedText());
|
||||
}
|
||||
|
||||
void HistoryInner::copyContextUrl() {
|
||||
@ -1217,9 +1259,12 @@ void HistoryInner::copyContextText() {
|
||||
return;
|
||||
}
|
||||
|
||||
QString contextMenuText = item->selectedText(FullSelection);
|
||||
if (!contextMenuText.isEmpty()) {
|
||||
QApplication::clipboard()->setText(contextMenuText);
|
||||
setToClipboard(item->selectedText(FullSelection));
|
||||
}
|
||||
|
||||
void HistoryInner::setToClipboard(const TextWithEntities &forClipboard) {
|
||||
if (auto data = mimeDataFromTextWithEntities(forClipboard)) {
|
||||
QApplication::clipboard()->setMimeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1227,42 +1272,48 @@ void HistoryInner::resizeEvent(QResizeEvent *e) {
|
||||
onUpdateSelected();
|
||||
}
|
||||
|
||||
QString HistoryInner::getSelectedText() const {
|
||||
TextWithEntities HistoryInner::getSelectedText() const {
|
||||
SelectedItems sel = _selected;
|
||||
|
||||
if (_dragAction == Selecting && _dragSelFrom && _dragSelTo) {
|
||||
applyDragSelection(&sel);
|
||||
}
|
||||
|
||||
if (sel.isEmpty()) return QString();
|
||||
if (sel.isEmpty()) {
|
||||
return TextWithEntities();
|
||||
}
|
||||
if (sel.cbegin().value() != FullSelection) {
|
||||
return sel.cbegin().key()->selectedText(sel.cbegin().value());
|
||||
}
|
||||
|
||||
int32 fullSize = 0, mtop = migratedTop(), htop = historyTop();
|
||||
int fullSize = 0;
|
||||
QString timeFormat(qsl(", [dd.MM.yy hh:mm]\n"));
|
||||
QMap<int32, QString> texts;
|
||||
for (SelectedItems::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
||||
QMap<int, TextWithEntities> texts;
|
||||
for (auto i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
||||
HistoryItem *item = i.key();
|
||||
if (item->detached()) continue;
|
||||
|
||||
QString text, sel = item->selectedText(FullSelection), time = item->date.toString(timeFormat);
|
||||
int32 size = item->author()->name.size() + time.size() + sel.size();
|
||||
text.reserve(size);
|
||||
QString time = item->date.toString(timeFormat);
|
||||
TextWithEntities part, unwrapped = item->selectedText(FullSelection);
|
||||
int size = item->author()->name.size() + time.size() + unwrapped.text.size();
|
||||
part.text.reserve(size);
|
||||
|
||||
int32 y = itemTop(item);
|
||||
int y = itemTop(item);
|
||||
if (y >= 0) {
|
||||
texts.insert(y, text.append(item->author()->name).append(time).append(sel));
|
||||
part.text.append(item->author()->name).append(time);
|
||||
appendTextWithEntities(part, std_::move(unwrapped));
|
||||
texts.insert(y, part);
|
||||
fullSize += size;
|
||||
}
|
||||
}
|
||||
|
||||
QString result, sep(qsl("\n\n"));
|
||||
result.reserve(fullSize + (texts.size() - 1) * 2);
|
||||
for (QMap<int32, QString>::const_iterator i = texts.cbegin(), e = texts.cend(); i != e; ++i) {
|
||||
result.append(i.value());
|
||||
TextWithEntities result;
|
||||
auto sep = qsl("\n\n");
|
||||
result.text.reserve(fullSize + (texts.size() - 1) * sep.size());
|
||||
for (auto i = texts.begin(), e = texts.end(); i != e; ++i) {
|
||||
appendTextWithEntities(result, std_::move(i.value()));
|
||||
if (i + 1 != e) {
|
||||
result.append(sep);
|
||||
result.text.append(sep);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -2027,7 +2078,7 @@ QString HistoryInner::tooltipText() const {
|
||||
} else if (_dragCursorState == HistoryInForwardedCursorState && _dragAction == NoDrag) {
|
||||
if (App::hoveredItem()) {
|
||||
if (HistoryMessageForwarded *fwd = App::hoveredItem()->Get<HistoryMessageForwarded>()) {
|
||||
return fwd->_text.original(AllTextSelection, ExpandLinksNone);
|
||||
return fwd->_text.originalText(AllTextSelection, ExpandLinksNone);
|
||||
}
|
||||
}
|
||||
} else if (ClickHandlerPtr lnk = ClickHandler::getActive()) {
|
||||
@ -2679,7 +2730,7 @@ bool HistoryHider::offerPeer(PeerId peer) {
|
||||
}
|
||||
|
||||
QString HistoryHider::offeredText() const {
|
||||
return toText.original();
|
||||
return toText.originalText();
|
||||
}
|
||||
|
||||
bool HistoryHider::wasOffered() const {
|
||||
@ -2772,32 +2823,6 @@ TextWithTags::Tags textTagsFromEntities(const EntitiesInText &entities) {
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// For mention tags save and validate userId, ignore tags for different userId.
|
||||
class FieldTagMimeProcessor : public FlatTextarea::TagMimeProcessor {
|
||||
public:
|
||||
QString mimeTagFromTag(const QString &tagId) override {
|
||||
if (tagId.startsWith(qstr("mention://"))) {
|
||||
return tagId + ':' + QString::number(MTP::authedId());
|
||||
}
|
||||
return tagId;
|
||||
}
|
||||
|
||||
QString tagFromMimeTag(const QString &mimeTag) override {
|
||||
if (mimeTag.startsWith(qstr("mention://"))) {
|
||||
auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
|
||||
if (!match.hasMatch() || match.capturedRef(1).toInt() != MTP::authedId()) {
|
||||
return QString();
|
||||
}
|
||||
return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
|
||||
}
|
||||
return mimeTag;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||
, _fieldBarCancel(this, st::replyCancel)
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
@ -6258,8 +6283,8 @@ void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, bool silent, const M
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
QString caption = item->getMedia() ? item->getMedia()->getCaption() : QString();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedPhoto(file, MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
|
||||
auto caption = item->getMedia() ? item->getMedia()->getCaption() : TextWithEntities();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedPhoto(file, MTP_string(caption.text)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6310,8 +6335,8 @@ void HistoryWidget::onDocumentUploaded(const FullMsgId &newId, bool silent, cons
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
QString caption = item->getMedia() ? item->getMedia()->getCaption() : QString();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedDocument(file, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
|
||||
auto caption = item->getMedia() ? item->getMedia()->getCaption() : TextWithEntities();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedDocument(file, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption.text)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6339,8 +6364,8 @@ void HistoryWidget::onThumbDocumentUploaded(const FullMsgId &newId, bool silent,
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
QString caption = item->getMedia() ? item->getMedia()->getCaption() : QString();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
|
||||
auto caption = item->getMedia() ? item->getMedia()->getCaption() : TextWithEntities();
|
||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), item->history()->peer->input, MTP_int(replyTo), MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->mime), _composeDocumentAttributes(document), MTP_string(caption.text)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, hist->sendRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7414,11 +7439,12 @@ void HistoryWidget::onEditMessage() {
|
||||
_history->clearMsgDraft();
|
||||
}
|
||||
|
||||
auto originalText = to->originalText();
|
||||
auto originalEntities = to->originalEntities();
|
||||
TextWithTags original = { textApplyEntities(originalText, originalEntities), textTagsFromEntities(originalEntities) };
|
||||
MessageCursor cursor = { original.text.size(), original.text.size(), QFIXED_MAX };
|
||||
_history->setEditDraft(std_::make_unique<HistoryEditDraft>(original, to->id, cursor, false));
|
||||
auto original = to->originalText();
|
||||
auto editText = textApplyEntities(original.text, original.entities);
|
||||
auto editTags = textTagsFromEntities(original.entities);
|
||||
TextWithTags editData = { editText, editTags };
|
||||
MessageCursor cursor = { editText.size(), editText.size(), QFIXED_MAX };
|
||||
_history->setEditDraft(std_::make_unique<HistoryEditDraft>(editData, to->id, cursor, false));
|
||||
applyDraft(false);
|
||||
|
||||
_previewData = 0;
|
||||
@ -7755,10 +7781,11 @@ void HistoryWidget::onCancel() {
|
||||
if (_inlineBotCancel) {
|
||||
onInlineBotCancel();
|
||||
} else if (_editMsgId) {
|
||||
auto originalText = _replyEditMsg ? _replyEditMsg->originalText() : QString();
|
||||
auto originalEntities = _replyEditMsg ? _replyEditMsg->originalEntities() : EntitiesInText();
|
||||
TextWithTags original = { textApplyEntities(originalText, originalEntities), textTagsFromEntities(originalEntities) };
|
||||
if (_replyEditMsg && original != _field.getTextWithTags()) {
|
||||
auto original = _replyEditMsg ? _replyEditMsg->originalText() : TextWithEntities();
|
||||
auto editText = textApplyEntities(original.text, original.entities);
|
||||
auto editTags = textTagsFromEntities(original.entities);
|
||||
TextWithTags editData = { editText, editTags };
|
||||
if (_replyEditMsg && editData != _field.getTextWithTags()) {
|
||||
auto box = new ConfirmBox(lang(lng_cancel_edit_post_sure), lang(lng_cancel_edit_post_yes), st::defaultBoxButton, lang(lng_cancel_edit_post_no));
|
||||
connect(box, SIGNAL(confirmed()), this, SLOT(onFieldBarCancel()));
|
||||
Ui::showLayer(box);
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
||||
|
||||
QString getSelectedText() const;
|
||||
TextWithEntities getSelectedText() const;
|
||||
|
||||
void dragActionStart(const QPoint &screenPos, Qt::MouseButton button = Qt::LeftButton);
|
||||
void dragActionUpdate(const QPoint &screenPos);
|
||||
@ -144,6 +144,8 @@ private:
|
||||
HistoryItem *nextItem(HistoryItem *item);
|
||||
void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false);
|
||||
|
||||
void setToClipboard(const TextWithEntities &forClipboard);
|
||||
|
||||
PeerData *_peer = nullptr;
|
||||
History *_migrated = nullptr;
|
||||
History *_history = nullptr;
|
||||
|
@ -3947,8 +3947,9 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
|
||||
|
||||
feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date
|
||||
if (peerId) {
|
||||
if (HistoryItem *item = App::histItemById(peerToChannel(peerId), d.vid.v)) {
|
||||
item->setText(text, d.has_entities() ? entitiesFromMTP(d.ventities.c_vector().v) : EntitiesInText());
|
||||
if (auto item = App::histItemById(peerToChannel(peerId), d.vid.v)) {
|
||||
auto entities = d.has_entities() ? entitiesFromMTP(d.ventities.c_vector().v) : EntitiesInText();
|
||||
item->setText({ text, entities });
|
||||
item->updateMedia(d.has_media() ? (&d.vmedia) : nullptr);
|
||||
item->addToOverview(AddToOverviewNew);
|
||||
}
|
||||
|
@ -912,7 +912,7 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) {
|
||||
_caption = Text();
|
||||
if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : nullptr) {
|
||||
if (HistoryPhoto *photoMsg = dynamic_cast<HistoryPhoto*>(itemMsg->getMedia())) {
|
||||
_caption.setText(st::mvCaptionFont, photoMsg->getCaption(), (item->author()->isUser() && item->author()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions);
|
||||
_caption.setMarkedText(st::mvCaptionFont, photoMsg->getCaption(), (item->author()->isUser() && item->author()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -898,9 +898,11 @@ bool Document::updateStatusText() const {
|
||||
Link::Link(HistoryMedia *media, HistoryItem *parent) : ItemBase(parent) {
|
||||
AddComponents(Info::Bit());
|
||||
|
||||
QString text = _parent->originalText(), mainUrl;
|
||||
EntitiesInText entities = _parent->originalEntities();
|
||||
const auto textWithEntities = _parent->originalText();
|
||||
QString mainUrl;
|
||||
|
||||
auto text = textWithEntities.text;
|
||||
auto &entities = textWithEntities.entities;
|
||||
int32 from = 0, till = text.size(), lnk = entities.size();
|
||||
for_const (const auto &entity, entities) {
|
||||
auto type = entity.type();
|
||||
|
@ -1011,7 +1011,8 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) {
|
||||
if (!_inner.document()->pageSize().isNull()) {
|
||||
_inner.document()->setPageSize(QSizeF(0, 0));
|
||||
}
|
||||
QTextCursor c(doc->docHandle(), replacePosition);
|
||||
QTextCursor c(doc->docHandle(), 0);
|
||||
c.setPosition(replacePosition);
|
||||
c.setPosition(replacePosition + replaceLen, QTextCursor::KeepAnchor);
|
||||
if (emoji) {
|
||||
insertEmoji(emoji, c);
|
||||
|
@ -463,7 +463,7 @@ void FlatTextarea::insertTag(const QString &text, QString tagId) {
|
||||
if (previousChar == '@' || previousChar == '#' || previousChar == '/') {
|
||||
if ((i == pos - fragmentPosition || (previousChar == '/' ? fragmentText.at(i).isLetterOrNumber() : fragmentText.at(i).isLetter()) || previousChar == '#') &&
|
||||
(i < 2 || !(fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_'))) {
|
||||
cursor.setPosition(fragmentPosition + i - 1, QTextCursor::MoveAnchor);
|
||||
cursor.setPosition(fragmentPosition + i - 1);
|
||||
int till = fragmentPosition + i;
|
||||
for (; (till < fragmentEnd) && (till - fragmentPosition - i + 1 < text.size()); ++till) {
|
||||
if (fragmentText.at(till - fragmentPosition).toLower() != text.at(till - fragmentPosition - i + 1).toLower()) {
|
||||
@ -608,6 +608,13 @@ public:
|
||||
_currentStart = currentPosition;
|
||||
};
|
||||
|
||||
void finish() {
|
||||
if (_currentTag < _tags->size()) {
|
||||
_tags->resize(_currentTag);
|
||||
_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FlatTextarea::TagList *_tags;
|
||||
bool _changed = false;
|
||||
@ -709,6 +716,8 @@ QString FlatTextarea::getTextPart(int start, int end, TagList *outTagsList, bool
|
||||
result.chop(1);
|
||||
|
||||
if (tillFragmentEnd) tagAccumulator.feed(QString(), result.size());
|
||||
tagAccumulator.finish();
|
||||
|
||||
if (outTagsChanged) {
|
||||
*outTagsChanged = tagAccumulator.changed();
|
||||
}
|
||||
@ -842,7 +851,8 @@ void FlatTextarea::insertFromMimeData(const QMimeData *source) {
|
||||
} else {
|
||||
_insertedTags.clear();
|
||||
}
|
||||
_realInsertPosition = textCursor().position();
|
||||
auto cursor = textCursor();
|
||||
_realInsertPosition = qMin(cursor.position(), cursor.anchor());
|
||||
_realCharsAdded = text.size();
|
||||
QTextEdit::insertFromMimeData(source);
|
||||
if (!_inDrop) {
|
||||
@ -895,7 +905,8 @@ void prepareFormattingOptimization(QTextDocument *document) {
|
||||
}
|
||||
|
||||
void removeTags(QTextDocument *document, int from, int end) {
|
||||
QTextCursor c(document->docHandle(), from);
|
||||
QTextCursor c(document->docHandle(), 0);
|
||||
c.setPosition(from);
|
||||
c.setPosition(end, QTextCursor::KeepAnchor);
|
||||
|
||||
QTextCharFormat format;
|
||||
@ -923,7 +934,8 @@ int processInsertedTags(QTextDocument *document, int changedPosition, int change
|
||||
if (applyNoTagFrom < tagFrom) {
|
||||
removeTags(document, applyNoTagFrom, tagFrom);
|
||||
}
|
||||
QTextCursor c(document->docHandle(), tagFrom);
|
||||
QTextCursor c(document->docHandle(), 0);
|
||||
c.setPosition(tagFrom);
|
||||
c.setPosition(tagTo, QTextCursor::KeepAnchor);
|
||||
|
||||
QTextCharFormat format;
|
||||
@ -1098,7 +1110,8 @@ void FlatTextarea::processFormatting(int insertPosition, int insertEnd) {
|
||||
if (action.type != ActionType::Invalid) {
|
||||
prepareFormattingOptimization(doc);
|
||||
|
||||
QTextCursor c(doc->docHandle(), action.intervalStart);
|
||||
QTextCursor c(doc->docHandle(), 0);
|
||||
c.setPosition(action.intervalStart);
|
||||
c.setPosition(action.intervalEnd, QTextCursor::KeepAnchor);
|
||||
if (action.type == ActionType::InsertEmoji) {
|
||||
insertEmoji(action.emoji, c);
|
||||
|
@ -555,14 +555,15 @@ public:
|
||||
}
|
||||
parse(options);
|
||||
}
|
||||
TextParser(Text *t, const QString &text, const EntitiesInText &preparsed, const TextParseOptions &options) : _t(t),
|
||||
src(text),
|
||||
TextParser(Text *t, const TextWithEntities &textWithEntities, const TextParseOptions &options) : _t(t),
|
||||
src(textWithEntities.text),
|
||||
rich(options.flags & TextParseRichText),
|
||||
multiline(options.flags & TextParseMultiline),
|
||||
maxLnkIndex(0),
|
||||
flags(0),
|
||||
lnkIndex(0),
|
||||
stopAfterWidth(QFIXED_MAX) {
|
||||
auto preparsed = textWithEntities.entities;
|
||||
if ((options.flags & TextParseLinks) && !preparsed.isEmpty()) {
|
||||
bool parseMentions = (options.flags & TextParseMentions);
|
||||
bool parseHashtags = (options.flags & TextParseHashtags);
|
||||
@ -573,7 +574,7 @@ public:
|
||||
} else {
|
||||
int32 i = 0, l = preparsed.size();
|
||||
entities.reserve(l);
|
||||
const QChar s = text.size();
|
||||
const QChar s = src.size();
|
||||
for (; i < l; ++i) {
|
||||
auto type = preparsed.at(i).type();
|
||||
if (((type == EntityInTextMention || type == EntityInTextMentionName) && !parseMentions) ||
|
||||
@ -2497,7 +2498,7 @@ void Text::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
|
||||
}
|
||||
}
|
||||
|
||||
void Text::setMarkedText(style::font font, const QString &text, const EntitiesInText &entities, const TextParseOptions &options) {
|
||||
void Text::setMarkedText(style::font font, const TextWithEntities &textWithEntities, const TextParseOptions &options) {
|
||||
if (!_textStyle) initDefault();
|
||||
_font = font;
|
||||
clear();
|
||||
@ -2532,7 +2533,7 @@ void Text::setMarkedText(style::font font, const QString &text, const EntitiesIn
|
||||
// newText.append("\n\n").append(text);
|
||||
// TextParser parser(this, newText, EntitiesInText(), options);
|
||||
|
||||
TextParser parser(this, text, entities, options);
|
||||
TextParser parser(this, textWithEntities, options);
|
||||
}
|
||||
recountNaturalSize(true, options.dir);
|
||||
}
|
||||
@ -2932,117 +2933,133 @@ TextSelection Text::adjustSelection(TextSelection selection, TextSelectType sele
|
||||
return { from, to };
|
||||
}
|
||||
|
||||
QString Text::original(TextSelection selection, ExpandLinksMode mode) const {
|
||||
template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
|
||||
void Text::enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const {
|
||||
if (isEmpty() || selection.empty()) {
|
||||
return QString();
|
||||
return;
|
||||
}
|
||||
|
||||
QString result, emptyurl;
|
||||
result.reserve(_text.size());
|
||||
|
||||
int32 lnkFrom = 0, lnkIndex = 0;
|
||||
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
|
||||
int32 blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
|
||||
int32 blockFrom = (i == e) ? _text.size() : (*i)->from();
|
||||
if (blockLnkIndex && !_links.at(blockLnkIndex - 1)) { // ignore empty links
|
||||
blockLnkIndex = 0;
|
||||
}
|
||||
if (blockLnkIndex != lnkIndex) {
|
||||
int32 rangeFrom = qMax(int32(selection.from), lnkFrom), rangeTo = qMin(blockFrom, int32(selection.to));
|
||||
if (lnkIndex && rangeTo > rangeFrom) { // write link
|
||||
QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom);
|
||||
if (lnkFrom != rangeFrom || blockFrom != rangeTo) {
|
||||
result += r;
|
||||
} else {
|
||||
QString expanded = _links.at(lnkIndex - 1)->getExpandedLinkText(mode, r);
|
||||
if (expanded.isEmpty()) {
|
||||
result += r;
|
||||
} else {
|
||||
result += expanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
lnkIndex = blockLnkIndex;
|
||||
lnkFrom = blockFrom;
|
||||
}
|
||||
if (i == e) break;
|
||||
|
||||
TextBlockType type = (*i)->type();
|
||||
if (type == TextBlockTSkip) continue;
|
||||
|
||||
if (!blockLnkIndex) {
|
||||
int32 rangeFrom = qMax(selection.from, (*i)->from()), rangeTo = qMin(selection.to, uint16((*i)->from() + TextPainter::_blockLength(this, i, e)));
|
||||
if (rangeTo > rangeFrom) {
|
||||
result += _text.midRef(rangeFrom, rangeTo - rangeFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
EntitiesInText Text::originalEntities() const {
|
||||
EntitiesInText result;
|
||||
int32 originalLength = 0, lnkStart = 0, italicStart = 0, boldStart = 0, codeStart = 0, preStart = 0;
|
||||
int32 lnkFrom = 0, lnkIndex = 0, flags = 0;
|
||||
for (TextBlocks::const_iterator i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
|
||||
int32 blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
|
||||
int32 blockFrom = (i == e) ? _text.size() : (*i)->from();
|
||||
int lnkIndex = 0;
|
||||
uint16 lnkFrom = 0;
|
||||
int32 flags = 0;
|
||||
for (auto i = _blocks.cbegin(), e = _blocks.cend(); true; ++i) {
|
||||
int blockLnkIndex = (i == e) ? 0 : (*i)->lnkIndex();
|
||||
uint16 blockFrom = (i == e) ? _text.size() : (*i)->from();
|
||||
int32 blockFlags = (i == e) ? 0 : (*i)->flags();
|
||||
if (blockFlags != flags) {
|
||||
if ((flags & TextBlockFItalic) && !(blockFlags & TextBlockFItalic)) { // write italic
|
||||
result.push_back(EntityInText(EntityInTextItalic, italicStart, originalLength - italicStart));
|
||||
} else if ((blockFlags & TextBlockFItalic) && !(flags & TextBlockFItalic)) {
|
||||
italicStart = originalLength;
|
||||
}
|
||||
if ((flags & TextBlockFSemibold) && !(blockFlags & TextBlockFSemibold)) {
|
||||
result.push_back(EntityInText(EntityInTextBold, boldStart, originalLength - boldStart));
|
||||
} else if ((blockFlags & TextBlockFSemibold) && !(flags & TextBlockFSemibold)) {
|
||||
boldStart = originalLength;
|
||||
}
|
||||
if ((flags & TextBlockFCode) && !(blockFlags & TextBlockFCode)) {
|
||||
result.push_back(EntityInText(EntityInTextCode, codeStart, originalLength - codeStart));
|
||||
} else if ((blockFlags & TextBlockFCode) && !(flags & TextBlockFCode)) {
|
||||
codeStart = originalLength;
|
||||
}
|
||||
if ((flags & TextBlockFPre) && !(blockFlags & TextBlockFPre)) {
|
||||
result.push_back(EntityInText(EntityInTextPre, preStart, originalLength - preStart));
|
||||
} else if ((blockFlags & TextBlockFPre) && !(flags & TextBlockFPre)) {
|
||||
preStart = originalLength;
|
||||
}
|
||||
|
||||
bool checkBlockFlags = (blockFrom >= selection.from) && (blockFrom <= selection.to);
|
||||
if (checkBlockFlags && blockFlags != flags) {
|
||||
flagsChangeCallback(flags, blockFlags);
|
||||
flags = blockFlags;
|
||||
}
|
||||
if (blockLnkIndex && !_links.at(blockLnkIndex - 1)) { // ignore empty links
|
||||
blockLnkIndex = 0;
|
||||
}
|
||||
if (blockLnkIndex != lnkIndex) {
|
||||
int32 rangeFrom = lnkFrom, rangeTo = blockFrom;
|
||||
if (lnkIndex && rangeTo > rangeFrom) { // write link
|
||||
QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom);
|
||||
if (auto entity = _links.at(lnkIndex - 1)->getEntityInText(lnkStart, r)) {
|
||||
result.push_back(entity);
|
||||
originalLength += entity.length();
|
||||
} else {
|
||||
originalLength += r.size();
|
||||
if (lnkIndex) {
|
||||
auto rangeFrom = qMax(selection.from, lnkFrom);
|
||||
auto rangeTo = qMin(blockFrom, selection.to);
|
||||
if (rangeTo > rangeFrom) { // handle click handler
|
||||
QStringRef r = _text.midRef(rangeFrom, rangeTo - rangeFrom);
|
||||
if (lnkFrom != rangeFrom || blockFrom != rangeTo) {
|
||||
appendPartCallback(r);
|
||||
} else {
|
||||
clickHandlerFinishCallback(r, _links.at(lnkIndex - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
lnkIndex = blockLnkIndex;
|
||||
if (lnkIndex) {
|
||||
lnkFrom = blockFrom;
|
||||
lnkStart = originalLength;
|
||||
clickHandlerStartCallback();
|
||||
}
|
||||
}
|
||||
if (i == e) break;
|
||||
if (i == e || blockFrom >= selection.to) break;
|
||||
|
||||
TextBlockType type = (*i)->type();
|
||||
if (type == TextBlockTSkip) continue;
|
||||
if ((*i)->type() == TextBlockTSkip) continue;
|
||||
|
||||
if (!blockLnkIndex) {
|
||||
int32 rangeFrom = (*i)->from(), rangeTo = uint16((*i)->from() + TextPainter::_blockLength(this, i, e));
|
||||
auto rangeFrom = qMax(selection.from, blockFrom);
|
||||
auto rangeTo = qMin(selection.to, uint16(blockFrom + TextPainter::_blockLength(this, i, e)));
|
||||
if (rangeTo > rangeFrom) {
|
||||
originalLength += rangeTo - rangeFrom;
|
||||
appendPartCallback(_text.midRef(rangeFrom, rangeTo - rangeFrom));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextWithEntities Text::originalTextWithEntities(TextSelection selection, ExpandLinksMode mode) const {
|
||||
TextWithEntities result;
|
||||
result.text.reserve(_text.size());
|
||||
|
||||
int lnkStart = 0, italicStart = 0, boldStart = 0, codeStart = 0, preStart = 0;
|
||||
auto flagsChangeCallback = [&result, &italicStart, &boldStart, &codeStart, &preStart](int32 oldFlags, int32 newFlags) {
|
||||
if ((oldFlags & TextBlockFItalic) && !(newFlags & TextBlockFItalic)) { // write italic
|
||||
result.entities.push_back(EntityInText(EntityInTextItalic, italicStart, result.text.size() - italicStart));
|
||||
} else if ((newFlags & TextBlockFItalic) && !(oldFlags & TextBlockFItalic)) {
|
||||
italicStart = result.text.size();
|
||||
}
|
||||
if ((oldFlags & TextBlockFSemibold) && !(newFlags & TextBlockFSemibold)) {
|
||||
result.entities.push_back(EntityInText(EntityInTextBold, boldStart, result.text.size() - boldStart));
|
||||
} else if ((newFlags & TextBlockFSemibold) && !(oldFlags & TextBlockFSemibold)) {
|
||||
boldStart = result.text.size();
|
||||
}
|
||||
if ((oldFlags & TextBlockFCode) && !(newFlags & TextBlockFCode)) {
|
||||
result.entities.push_back(EntityInText(EntityInTextCode, codeStart, result.text.size() - codeStart));
|
||||
} else if ((newFlags & TextBlockFCode) && !(oldFlags & TextBlockFCode)) {
|
||||
codeStart = result.text.size();
|
||||
}
|
||||
if ((oldFlags & TextBlockFPre) && !(newFlags & TextBlockFPre)) {
|
||||
result.entities.push_back(EntityInText(EntityInTextPre, preStart, result.text.size() - preStart));
|
||||
} else if ((newFlags & TextBlockFPre) && !(oldFlags & TextBlockFPre)) {
|
||||
preStart = result.text.size();
|
||||
}
|
||||
};
|
||||
auto clickHandlerStartCallback = [&result, &lnkStart]() {
|
||||
lnkStart = result.text.size();
|
||||
};
|
||||
auto clickHandlerFinishCallback = [mode, &result, &lnkStart](const QStringRef &r, const ClickHandlerPtr &handler) {
|
||||
auto expanded = handler->getExpandedLinkTextWithEntities(mode, lnkStart, r);
|
||||
if (expanded.text.isEmpty()) {
|
||||
result.text += r;
|
||||
} else {
|
||||
result.text += expanded.text;
|
||||
}
|
||||
if (!expanded.entities.isEmpty()) {
|
||||
result.entities.append(expanded.entities);
|
||||
}
|
||||
};
|
||||
auto appendPartCallback = [&result](const QStringRef &r) {
|
||||
result.text += r;
|
||||
};
|
||||
|
||||
enumerateText(selection, appendPartCallback, clickHandlerStartCallback, clickHandlerFinishCallback, flagsChangeCallback);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString Text::originalText(TextSelection selection, ExpandLinksMode mode) const {
|
||||
QString result;
|
||||
result.reserve(_text.size());
|
||||
|
||||
auto appendPartCallback = [&result](const QStringRef &r) {
|
||||
result += r;
|
||||
};
|
||||
auto clickHandlerStartCallback = []() {
|
||||
};
|
||||
auto clickHandlerFinishCallback = [mode, &result](const QStringRef &r, const ClickHandlerPtr &handler) {
|
||||
auto expanded = handler->getExpandedLinkText(mode, r);
|
||||
if (expanded.isEmpty()) {
|
||||
result += r;
|
||||
} else {
|
||||
result += expanded;
|
||||
}
|
||||
};
|
||||
auto flagsChangeCallback = [](int32 oldFlags, int32 newFlags) {
|
||||
};
|
||||
|
||||
enumerateText(selection, appendPartCallback, clickHandlerStartCallback, clickHandlerFinishCallback, flagsChangeCallback);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ public:
|
||||
int32 countHeight(int32 width) const;
|
||||
void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions);
|
||||
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 setMarkedText(style::font font, const TextWithEntities &textWithEntities, const TextParseOptions &options = _defaultOptions);
|
||||
|
||||
void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk);
|
||||
bool hasLinks() const;
|
||||
@ -178,8 +178,9 @@ public:
|
||||
int length() const {
|
||||
return _text.size();
|
||||
}
|
||||
QString original(TextSelection selection = AllTextSelection, ExpandLinksMode mode = ExpandLinksShortened) const;
|
||||
EntitiesInText originalEntities() const;
|
||||
|
||||
TextWithEntities originalTextWithEntities(TextSelection selection = AllTextSelection, ExpandLinksMode mode = ExpandLinksShortened) const;
|
||||
QString originalText(TextSelection selection = AllTextSelection, ExpandLinksMode mode = ExpandLinksShortened) const;
|
||||
|
||||
bool lastDots(int32 dots, int32 maxdots = 3) { // hack for typing animation
|
||||
if (_text.size() < maxdots) return false;
|
||||
@ -207,6 +208,10 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
// Template method for originalText(), originalTextWithEntities().
|
||||
template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
|
||||
void enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const;
|
||||
|
||||
void recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir = Qt::LayoutDirectionAuto);
|
||||
|
||||
// clear() deletes all blocks and calls this method
|
||||
|
@ -1437,7 +1437,7 @@ MTPVector<MTPMessageEntity> linksToMTP(const EntitiesInText &links, bool sending
|
||||
|
||||
// Some code is duplicated in flattextarea.cpp!
|
||||
void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities, bool rich) {
|
||||
EntitiesInText mono;
|
||||
EntitiesInText result;
|
||||
|
||||
bool withHashtags = (flags & TextParseHashtags);
|
||||
bool withMentions = (flags & TextParseMentions);
|
||||
@ -1445,6 +1445,9 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities
|
||||
bool withMono = (flags & TextParseMono);
|
||||
|
||||
if (withMono) { // parse mono entities (code and pre)
|
||||
int existingEntityIndex = 0, existingEntitiesCount = inOutEntities->size();
|
||||
int existingEntityShiftLeft = 0;
|
||||
|
||||
QString newText;
|
||||
|
||||
int32 offset = 0, matchOffset = offset, len = text.size(), commandOffset = rich ? 0 : len;
|
||||
@ -1468,7 +1471,7 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities
|
||||
auto mCode = _reCode.match(text, matchOffset);
|
||||
if (!mPre.hasMatch() && !mCode.hasMatch()) break;
|
||||
|
||||
int32 preStart = mPre.hasMatch() ? mPre.capturedStart() : INT_MAX,
|
||||
int preStart = mPre.hasMatch() ? mPre.capturedStart() : INT_MAX,
|
||||
preEnd = mPre.hasMatch() ? mPre.capturedEnd() : INT_MAX,
|
||||
codeStart = mCode.hasMatch() ? mCode.capturedStart() : INT_MAX,
|
||||
codeEnd = mCode.hasMatch() ? mCode.capturedEnd() : INT_MAX,
|
||||
@ -1506,11 +1509,25 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newText.isEmpty()) newText.reserve(text.size());
|
||||
|
||||
bool addNewlineBefore = false, addNewlineAfter = false;
|
||||
int32 outerStart = tagStart, outerEnd = tagEnd;
|
||||
int32 innerStart = tagStart + mTag.capturedLength(2), innerEnd = tagEnd - mTag.capturedLength(3);
|
||||
|
||||
// Check if start or end sequences intersect any existing entity.
|
||||
int intersectedEntityEnd = 0;
|
||||
for_const (auto &entity, *inOutEntities) {
|
||||
if (qMin(innerStart, entity.offset() + entity.length()) > qMax(outerStart, entity.offset()) ||
|
||||
qMin(outerEnd, entity.offset() + entity.length()) > qMax(innerEnd, entity.offset())) {
|
||||
intersectedEntityEnd = entity.offset() + entity.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (intersectedEntityEnd > 0) {
|
||||
matchOffset = qMax(innerStart, intersectedEntityEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newText.isEmpty()) newText.reserve(text.size());
|
||||
if (pre) {
|
||||
while (outerStart > 0 && chIsSpace(*(start + outerStart - 1), rich) && !chIsNewline(*(start + outerStart - 1))) {
|
||||
--outerStart;
|
||||
@ -1540,14 +1557,27 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities
|
||||
}
|
||||
addNewlineAfter = (outerEnd < len && !chIsNewline(*(start + outerEnd)));
|
||||
}
|
||||
|
||||
for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() < innerStart; ++existingEntityIndex) {
|
||||
auto &entity = inOutEntities->at(existingEntityIndex);
|
||||
result.push_back(entity);
|
||||
result.back().shiftLeft(existingEntityShiftLeft);
|
||||
}
|
||||
if (outerStart > offset) newText.append(start + offset, outerStart - offset);
|
||||
if (addNewlineBefore) newText.append('\n');
|
||||
existingEntityShiftLeft += (innerStart - outerStart) - (addNewlineBefore ? 1 : 0);
|
||||
|
||||
int32 tagLength = innerEnd - innerStart;
|
||||
mono.push_back(EntityInText(pre ? EntityInTextPre : EntityInTextCode, newText.size(), tagLength));
|
||||
int entityStart = newText.size(), entityLength = innerEnd - innerStart;
|
||||
result.push_back(EntityInText(pre ? EntityInTextPre : EntityInTextCode, entityStart, entityLength));
|
||||
|
||||
newText.append(start + innerStart, tagLength);
|
||||
for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() <= innerEnd; ++existingEntityIndex) {
|
||||
auto &entity = inOutEntities->at(existingEntityIndex);
|
||||
result.push_back(entity);
|
||||
result.back().shiftLeft(existingEntityShiftLeft);
|
||||
}
|
||||
newText.append(start + innerStart, entityLength);
|
||||
if (addNewlineAfter) newText.append('\n');
|
||||
existingEntityShiftLeft += (outerEnd - innerEnd) - (addNewlineAfter ? 1 : 0);
|
||||
|
||||
offset = matchOffset = outerEnd;
|
||||
}
|
||||
@ -1555,8 +1585,19 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities
|
||||
newText.append(start + offset, len - offset);
|
||||
text = newText;
|
||||
}
|
||||
if (!result.isEmpty()) {
|
||||
for (; existingEntityIndex < existingEntitiesCount; ++existingEntityIndex) {
|
||||
auto &entity = inOutEntities->at(existingEntityIndex);
|
||||
result.push_back(entity);
|
||||
result.back().shiftLeft(existingEntityShiftLeft);
|
||||
}
|
||||
*inOutEntities = result;
|
||||
result = EntitiesInText();
|
||||
}
|
||||
}
|
||||
int32 monoEntity = 0, monoCount = mono.size(), monoTill = 0;
|
||||
|
||||
int existingEntityIndex = 0, existingEntitiesCount = inOutEntities->size();
|
||||
int existingEntityEnd = 0;
|
||||
|
||||
initLinkSets();
|
||||
int32 len = text.size(), commandOffset = rich ? 0 : len;
|
||||
@ -1572,11 +1613,11 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities
|
||||
}
|
||||
}
|
||||
}
|
||||
QRegularExpressionMatch mDomain = _reDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
|
||||
QRegularExpressionMatch mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
|
||||
QRegularExpressionMatch mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
auto mDomain = _reDomain.match(text, matchOffset);
|
||||
auto mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
|
||||
auto mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
auto mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
|
||||
auto mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch();
|
||||
|
||||
EntityInTextType lnkType = EntityInTextUrl;
|
||||
int32 lnkStart = 0, lnkLength = 0;
|
||||
@ -1735,19 +1776,23 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities
|
||||
lnkLength = (p - start) - lnkStart;
|
||||
}
|
||||
}
|
||||
for (; monoEntity < monoCount && mono[monoEntity].offset() <= lnkStart; ++monoEntity) {
|
||||
monoTill = qMax(monoTill, mono[monoEntity].offset() + mono[monoEntity].length());
|
||||
inOutEntities->push_back(mono[monoEntity]);
|
||||
for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() <= lnkStart; ++existingEntityIndex) {
|
||||
auto &entity = inOutEntities->at(existingEntityIndex);
|
||||
accumulate_max(existingEntityEnd, entity.offset() + entity.length());
|
||||
result.push_back(entity);
|
||||
}
|
||||
if (lnkStart >= monoTill) {
|
||||
if (lnkStart >= existingEntityEnd) {
|
||||
inOutEntities->push_back(EntityInText(lnkType, lnkStart, lnkLength));
|
||||
}
|
||||
|
||||
offset = matchOffset = lnkStart + lnkLength;
|
||||
}
|
||||
for (; monoEntity < monoCount; ++monoEntity) {
|
||||
monoTill = qMax(monoTill, mono[monoEntity].offset() + mono[monoEntity].length());
|
||||
inOutEntities->push_back(mono[monoEntity]);
|
||||
if (!result.isEmpty()) {
|
||||
for (; existingEntityIndex < existingEntitiesCount; ++existingEntityIndex) {
|
||||
auto &entity = inOutEntities->at(existingEntityIndex);
|
||||
result.push_back(entity);
|
||||
}
|
||||
*inOutEntities = result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,9 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
void shiftRight(int shift) {
|
||||
_offset += shift;
|
||||
}
|
||||
void updateTextEnd(int textEnd) {
|
||||
if (_offset > textEnd) {
|
||||
_offset = textEnd;
|
||||
@ -108,7 +111,19 @@ private:
|
||||
QString _data;
|
||||
|
||||
};
|
||||
typedef QList<EntityInText> EntitiesInText;
|
||||
|
||||
struct TextWithEntities {
|
||||
QString text;
|
||||
EntitiesInText entities;
|
||||
};
|
||||
inline void appendTextWithEntities(TextWithEntities &to, TextWithEntities &&append) {
|
||||
int entitiesShiftRight = to.text.size();
|
||||
for (auto &entity : append.entities) {
|
||||
entity.shiftRight(entitiesShiftRight);
|
||||
}
|
||||
to.text += append.text;
|
||||
to.entities += append.entities;
|
||||
}
|
||||
|
||||
// text preprocess
|
||||
QString textClean(const QString &text);
|
||||
|
Loading…
Reference in New Issue
Block a user