Some refactoring in working with text entities.

Also move this code to TextUtilities namespace.
This commit is contained in:
John Preston 2017-07-06 14:37:42 +03:00
parent f38fad2f92
commit da0d78135d
44 changed files with 1003 additions and 984 deletions

View File

@ -87,7 +87,7 @@ void ApiWrap::addLocalChangelogs(int oldAppVersion) {
auto addedSome = false; auto addedSome = false;
auto addLocalChangelog = [this, &addedSome](const QString &text) { auto addLocalChangelog = [this, &addedSome](const QString &text) {
auto textWithEntities = TextWithEntities { text }; auto textWithEntities = TextWithEntities { text };
textParseEntities(textWithEntities.text, TextParseLinks, &textWithEntities.entities); TextUtilities::ParseEntities(textWithEntities, TextParseLinks);
App::wnd()->serviceNotification(textWithEntities, MTP_messageMediaEmpty(), unixtime()); App::wnd()->serviceNotification(textWithEntities, MTP_messageMediaEmpty(), unixtime());
addedSome = true; addedSome = true;
}; };
@ -1177,7 +1177,7 @@ void ApiWrap::saveDraftsToCloud() {
if (!textWithTags.tags.isEmpty()) { if (!textWithTags.tags.isEmpty()) {
flags |= MTPmessages_SaveDraft::Flag::f_entities; flags |= MTPmessages_SaveDraft::Flag::f_entities;
} }
auto entities = linksToMTP(ConvertTextTagsToEntities(textWithTags.tags), true); auto entities = TextUtilities::EntitiesToMTP(ConvertTextTagsToEntities(textWithTags.tags), TextUtilities::ConvertOption::SkipLocal);
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(MTP_flags(flags), MTP_int(cloudDraft->msgId), history->peer->input, MTP_string(textWithTags.text), entities)).done([this, history](const MTPBool &result, mtpRequestId requestId) { cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(MTP_flags(flags), MTP_int(cloudDraft->msgId), history->peer->input, MTP_string(textWithTags.text), entities)).done([this, history](const MTPBool &result, mtpRequestId requestId) {
if (auto cloudDraft = history->cloudDraft()) { if (auto cloudDraft = history->cloudDraft()) {

View File

@ -459,11 +459,11 @@ namespace {
// apply first_name and last_name from minimal user only if we don't have // apply first_name and last_name from minimal user only if we don't have
// local values for first name and last name already, otherwise skip // local values for first name and last name already, otherwise skip
bool noLocalName = data->firstName.isEmpty() && data->lastName.isEmpty(); bool noLocalName = data->firstName.isEmpty() && data->lastName.isEmpty();
QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName; QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? TextUtilities::SingleLine(qs(d.vfirst_name)) : QString()) : data->firstName;
QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName; QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? TextUtilities::SingleLine(qs(d.vlast_name)) : QString()) : data->lastName;
QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString()); QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString());
QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString()); QString uname = minimal ? data->username : (d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString());
bool phoneChanged = (data->phone() != phone); bool phoneChanged = (data->phone() != phone);
if (phoneChanged) { if (phoneChanged) {
@ -714,7 +714,7 @@ namespace {
} }
cdata->flagsUpdated(); cdata->flagsUpdated();
QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString(); QString uname = d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString();
cdata->setName(qs(d.vtitle), uname); cdata->setName(qs(d.vtitle), uname);
cdata->setIsForbidden(false); cdata->setIsForbidden(false);
@ -1096,7 +1096,7 @@ namespace {
} }
if (auto existing = App::histItemById(peerToChannel(peerId), m.vid.v)) { if (auto existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
auto text = qs(m.vmessage); auto text = qs(m.vmessage);
auto entities = m.has_entities() ? entitiesFromMTP(m.ventities.v) : EntitiesInText(); auto entities = m.has_entities() ? TextUtilities::EntitiesFromMTP(m.ventities.v) : EntitiesInText();
existing->setText({ text, entities }); existing->setText({ text, entities });
existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr); existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr);
existing->updateReplyMarkup(m.has_reply_markup() ? (&m.vreply_markup) : nullptr); existing->updateReplyMarkup(m.has_reply_markup() ? (&m.vreply_markup) : nullptr);
@ -1334,7 +1334,7 @@ namespace {
bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact; bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact;
bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone)); bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone));
if (showPhoneChanged) { if (showPhoneChanged) {
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), textOneLine(user->username)); user->setName(TextUtilities::SingleLine(user->firstName), TextUtilities::SingleLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), TextUtilities::SingleLine(user->username));
} }
markPeerUpdated(user); markPeerUpdated(user);
} }
@ -1497,13 +1497,13 @@ namespace {
} }
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) { WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) {
auto description = TextWithEntities { webpage.has_description() ? textClean(qs(webpage.vdescription)) : QString() }; auto description = TextWithEntities { webpage.has_description() ? TextUtilities::Clean(qs(webpage.vdescription)) : QString() };
auto siteName = webpage.has_site_name() ? qs(webpage.vsite_name) : QString(); auto siteName = webpage.has_site_name() ? qs(webpage.vsite_name) : QString();
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
if (siteName == qstr("Twitter") || siteName == qstr("Instagram")) { if (siteName == qstr("Twitter") || siteName == qstr("Instagram")) {
parseFlags |= TextParseHashtags | TextParseMentions; parseFlags |= TextParseHashtags | TextParseMentions;
} }
textParseEntities(description.text, parseFlags, &description.entities); TextUtilities::ParseEntities(description, parseFlags);
return App::webPageSet(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), siteName, webpage.has_title() ? qs(webpage.vtitle) : QString(), description, webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : nullptr, webpage.has_document() ? App::feedDocument(webpage.vdocument) : nullptr, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString(), 0); return App::webPageSet(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), siteName, webpage.has_title() ? qs(webpage.vtitle) : QString(), description, webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : nullptr, webpage.has_document() ? App::feedDocument(webpage.vdocument) : nullptr, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString(), 0);
} }
@ -1800,15 +1800,15 @@ namespace {
} }
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) { if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) {
convert->type = toWebPageType(type); convert->type = toWebPageType(type);
convert->url = textClean(url); convert->url = TextUtilities::Clean(url);
convert->displayUrl = textClean(displayUrl); convert->displayUrl = TextUtilities::Clean(displayUrl);
convert->siteName = textClean(siteName); convert->siteName = TextUtilities::Clean(siteName);
convert->title = textOneLine(textClean(title)); convert->title = TextUtilities::SingleLine(title);
convert->description = description; convert->description = description;
convert->photo = photo; convert->photo = photo;
convert->document = document; convert->document = document;
convert->duration = duration; convert->duration = duration;
convert->author = textClean(author); convert->author = TextUtilities::Clean(author);
if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert); if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert);
convert->pendingTill = pendingTill; convert->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(convert); if (App::main()) App::main()->webPageUpdated(convert);
@ -1831,15 +1831,15 @@ namespace {
if (result != convert) { if (result != convert) {
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) { if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) {
result->type = toWebPageType(type); result->type = toWebPageType(type);
result->url = textClean(url); result->url = TextUtilities::Clean(url);
result->displayUrl = textClean(displayUrl); result->displayUrl = TextUtilities::Clean(displayUrl);
result->siteName = textClean(siteName); result->siteName = TextUtilities::Clean(siteName);
result->title = textOneLine(textClean(title)); result->title = TextUtilities::SingleLine(title);
result->description = description; result->description = description;
result->photo = photo; result->photo = photo;
result->document = document; result->document = document;
result->duration = duration; result->duration = duration;
result->author = textClean(author); result->author = TextUtilities::Clean(author);
if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result); if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result);
result->pendingTill = pendingTill; result->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(result); if (App::main()) App::main()->webPageUpdated(result);
@ -1869,9 +1869,9 @@ namespace {
} }
if (!convert->accessHash && accessHash) { if (!convert->accessHash && accessHash) {
convert->accessHash = accessHash; convert->accessHash = accessHash;
convert->shortName = textClean(shortName); convert->shortName = TextUtilities::Clean(shortName);
convert->title = textOneLine(textClean(title)); convert->title = TextUtilities::SingleLine(title);
convert->description = textClean(description); convert->description = TextUtilities::Clean(description);
convert->photo = photo; convert->photo = photo;
convert->document = document; convert->document = document;
if (App::main()) App::main()->gameUpdated(convert); if (App::main()) App::main()->gameUpdated(convert);
@ -1891,9 +1891,9 @@ namespace {
if (result != convert) { if (result != convert) {
if (!result->accessHash && accessHash) { if (!result->accessHash && accessHash) {
result->accessHash = accessHash; result->accessHash = accessHash;
result->shortName = textClean(shortName); result->shortName = TextUtilities::Clean(shortName);
result->title = textOneLine(textClean(title)); result->title = TextUtilities::SingleLine(title);
result->description = textClean(description); result->description = TextUtilities::Clean(description);
result->photo = photo; result->photo = photo;
result->document = document; result->document = document;
if (App::main()) App::main()->gameUpdated(result); if (App::main()) App::main()->gameUpdated(result);

View File

@ -173,9 +173,9 @@ void AddContactBox::onSubmit() {
void AddContactBox::onSave() { void AddContactBox::onSave() {
if (_addRequest) return; if (_addRequest) return;
QString firstName = prepareText(_first->getLastText()); auto firstName = TextUtilities::PrepareForSending(_first->getLastText());
QString lastName = prepareText(_last->getLastText()); auto lastName = TextUtilities::PrepareForSending(_last->getLastText());
QString phone = _phone->getLastText().trimmed(); auto phone = _phone->getLastText().trimmed();
if (firstName.isEmpty() && lastName.isEmpty()) { if (firstName.isEmpty() && lastName.isEmpty()) {
if (_invertOrder) { if (_invertOrder) {
_last->setFocus(); _last->setFocus();
@ -368,8 +368,8 @@ void GroupInfoBox::onNameSubmit() {
void GroupInfoBox::onNext() { void GroupInfoBox::onNext() {
if (_creationRequestId) return; if (_creationRequestId) return;
auto title = prepareText(_title->getLastText()); auto title = TextUtilities::PrepareForSending(_title->getLastText());
auto description = _description ? prepareText(_description->getLastText(), true) : QString(); auto description = _description ? TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks) : QString();
if (title.isEmpty()) { if (title.isEmpty()) {
_title->setFocus(); _title->setFocus();
_title->showError(); _title->showError();
@ -695,7 +695,7 @@ void SetupChannelBox::privacyChanged(Privacy value) {
} }
void SetupChannelBox::onUpdateDone(const MTPBool &result) { void SetupChannelBox::onUpdateDone(const MTPBool &result) {
_channel->setName(textOneLine(_channel->name), _sentUsername); _channel->setName(TextUtilities::SingleLine(_channel->name), _sentUsername);
closeBox(); closeBox();
} }
@ -705,7 +705,7 @@ bool SetupChannelBox::onUpdateFail(const RPCError &error) {
_saveRequestId = 0; _saveRequestId = 0;
QString err(error.type()); QString err(error.type());
if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == _channel->username) { if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == _channel->username) {
_channel->setName(textOneLine(_channel->name), textOneLine(_sentUsername)); _channel->setName(TextUtilities::SingleLine(_channel->name), TextUtilities::SingleLine(_sentUsername));
closeBox(); closeBox();
return true; return true;
} else if (err == "USERNAME_INVALID") { } else if (err == "USERNAME_INVALID") {
@ -872,7 +872,8 @@ void EditNameTitleBox::resizeEvent(QResizeEvent *e) {
void EditNameTitleBox::onSave() { void EditNameTitleBox::onSave() {
if (_requestId) return; if (_requestId) return;
QString first = prepareText(_first->getLastText()), last = prepareText(_last->getLastText()); auto first = TextUtilities::PrepareForSending(_first->getLastText());
auto last = TextUtilities::PrepareForSending(_last->getLastText());
if (first.isEmpty() && last.isEmpty()) { if (first.isEmpty() && last.isEmpty()) {
if (_invertOrder) { if (_invertOrder) {
_last->setFocus(); _last->setFocus();
@ -904,10 +905,11 @@ void EditNameTitleBox::onSaveSelfDone(const MTPUser &user) {
bool EditNameTitleBox::onSaveSelfFail(const RPCError &error) { bool EditNameTitleBox::onSaveSelfFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false; if (MTP::isDefaultHandledError(error)) return false;
QString err(error.type()); auto err = error.type();
QString first = textOneLine(_first->getLastText().trimmed()), last = textOneLine(_last->getLastText().trimmed()); auto first = TextUtilities::SingleLine(_first->getLastText().trimmed());
auto last = TextUtilities::SingleLine(_last->getLastText().trimmed());
if (err == "NAME_NOT_MODIFIED") { if (err == "NAME_NOT_MODIFIED") {
App::self()->setName(first, last, QString(), textOneLine(App::self()->username)); App::self()->setName(first, last, QString(), TextUtilities::SingleLine(App::self()->username));
closeBox(); closeBox();
return true; return true;
} else if (err == "FIRSTNAME_INVALID") { } else if (err == "FIRSTNAME_INVALID") {
@ -1073,7 +1075,8 @@ void EditChannelBox::paintEvent(QPaintEvent *e) {
void EditChannelBox::onSave() { void EditChannelBox::onSave() {
if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId || _saveInvitesRequestId) return; if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId || _saveInvitesRequestId) return;
QString title = prepareText(_title->getLastText()), description = prepareText(_description->getLastText(), true); auto title = TextUtilities::PrepareForSending(_title->getLastText());
auto description = TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks);
if (title.isEmpty()) { if (title.isEmpty()) {
_title->setFocus(); _title->setFocus();
_title->showError(); _title->showError();

View File

@ -1526,22 +1526,11 @@ void ContactsBox::Inner::updateSelection() {
void ContactsBox::Inner::updateFilter(QString filter) { void ContactsBox::Inner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed(); _lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
auto words = TextUtilities::PrepareSearchWords(_lastQuery);
filter = words.isEmpty() ? QString() : words.join(' ');
_time = unixtime(); _time = unixtime();
QStringList f;
if (!filter.isEmpty()) {
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
if (_filter != filter) { if (_filter != filter) {
_filter = filter; _filter = filter;
@ -1560,10 +1549,10 @@ void ContactsBox::Inner::updateFilter(QString filter) {
} else { } else {
if (!_addContactLnk->isHidden()) _addContactLnk->hide(); if (!_addContactLnk->isHidden()) _addContactLnk->hide();
if (!_allAdmins->isHidden()) _allAdmins->hide(); if (!_allAdmins->isHidden()) _allAdmins->hide();
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
_filtered.clear(); _filtered.clear();
if (!f.isEmpty()) { if (!words.isEmpty()) {
const Dialogs::List *toFilter = nullptr; const Dialogs::List *toFilter = nullptr;
if (!_contacts->isEmpty()) { if (!_contacts->isEmpty()) {
for (fi = fb; fi != fe; ++fi) { for (fi = fb; fi != fe; ++fi) {

View File

@ -984,7 +984,7 @@ void PeerListBox::Inner::checkScrollForPreload() {
} }
void PeerListBox::Inner::searchQueryChanged(QString query) { void PeerListBox::Inner::searchQueryChanged(QString query) {
auto searchWordsList = query.isEmpty() ? QStringList() : query.split(cWordSplit(), QString::SkipEmptyParts); auto searchWordsList = TextUtilities::PrepareSearchWords(query);
auto normalizedQuery = searchWordsList.isEmpty() ? QString() : searchWordsList.join(' '); auto normalizedQuery = searchWordsList.isEmpty() ? QString() : searchWordsList.join(' ');
if (_normalizedSearchQuery != normalizedQuery) { if (_normalizedSearchQuery != normalizedQuery) {
setSearchQuery(query, normalizedQuery); setSearchQuery(query, normalizedQuery);

View File

@ -438,7 +438,7 @@ void SendFilesBox::onSend(bool ctrlShiftEnter) {
_confirmed = true; _confirmed = true;
if (_confirmedCallback) { if (_confirmedCallback) {
auto compressed = _compressed ? _compressed->checked() : false; auto compressed = _compressed ? _compressed->checked() : false;
auto caption = _caption ? prepareText(_caption->getLastText(), true) : QString(); auto caption = _caption ? TextUtilities::PrepareForSending(_caption->getLastText(), TextUtilities::PrepareTextOption::CheckLinks) : QString();
_confirmedCallback(_files, _animated ? QImage() : _image, std::move(_information), compressed, caption, ctrlShiftEnter); _confirmedCallback(_files, _animated ? QImage() : _image, std::move(_information), compressed, caption, ctrlShiftEnter);
} }
closeBox(); closeBox();
@ -766,7 +766,7 @@ void EditCaptionBox::onSave(bool ctrlShiftEnter) {
if (!sentEntities.v.isEmpty()) { if (!sentEntities.v.isEmpty()) {
flags |= MTPmessages_EditMessage::Flag::f_entities; flags |= MTPmessages_EditMessage::Flag::f_entities;
} }
auto text = prepareText(_field->getLastText(), true); auto text = TextUtilities::PrepareForSending(_field->getLastText(), TextUtilities::PrepareTextOption::CheckLinks);
_saveRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(flags), item->history()->peer->input, MTP_int(item->id), MTP_string(text), MTPnullMarkup, sentEntities), rpcDone(&EditCaptionBox::saveDone), rpcFail(&EditCaptionBox::saveFail)); _saveRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(flags), item->history()->peer->input, MTP_int(item->id), MTP_string(text), MTPnullMarkup, sentEntities), rpcDone(&EditCaptionBox::saveDone), rpcFail(&EditCaptionBox::saveFail));
} }

View File

@ -674,21 +674,9 @@ bool ShareBox::Inner::hasSelected() const {
void ShareBox::Inner::updateFilter(QString filter) { void ShareBox::Inner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed(); _lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
QStringList f; auto words = TextUtilities::PrepareSearchWords(_lastQuery);
if (!filter.isEmpty()) { filter = words.isEmpty() ? QString() : words.join(' ');
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
if (_filter != filter) { if (_filter != filter) {
_filter = filter; _filter = filter;
@ -701,10 +689,10 @@ void ShareBox::Inner::updateFilter(QString filter) {
if (_filter.isEmpty()) { if (_filter.isEmpty()) {
refresh(); refresh();
} else { } else {
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
_filtered.clear(); _filtered.clear();
if (!f.isEmpty()) { if (!words.isEmpty()) {
const Dialogs::List *toFilter = nullptr; const Dialogs::List *toFilter = nullptr;
if (!_chatsIndexed->isEmpty()) { if (!_chatsIndexed->isEmpty()) {
for (fi = fb; fi != fe; ++fi) { for (fi = fb; fi != fe; ++fi) {

View File

@ -428,18 +428,17 @@ bool StickerSetBox::Inner::official() const {
} }
base::lambda<TextWithEntities()> StickerSetBox::Inner::title() const { base::lambda<TextWithEntities()> StickerSetBox::Inner::title() const {
auto text = _setTitle; auto text = TextWithEntities { _setTitle };
auto entities = EntitiesInText();
if (_loaded) { if (_loaded) {
if (_pack.isEmpty()) { if (_pack.isEmpty()) {
return [] { return TextWithEntities { lang(lng_attach_failed), EntitiesInText() }; }; return [] { return TextWithEntities { lang(lng_attach_failed), EntitiesInText() }; };
} else { } else {
textParseEntities(text, TextParseMentions, &entities); TextUtilities::ParseEntities(text, TextParseMentions);
} }
} else { } else {
return [] { return TextWithEntities { lang(lng_contacts_loading), EntitiesInText() }; }; return [] { return TextWithEntities { lang(lng_contacts_loading), EntitiesInText() }; };
} }
return [text, entities] { return TextWithEntities { text, entities }; }; return [text] { return text; };
} }
QString StickerSetBox::Inner::shortName() const { QString StickerSetBox::Inner::shortName() const {

View File

@ -175,7 +175,7 @@ bool UsernameBox::onUpdateFail(const RPCError &error) {
_saveRequestId = 0; _saveRequestId = 0;
QString err(error.type()); QString err(error.type());
if (err == qstr("USERNAME_NOT_MODIFIED") || _sentUsername == App::self()->username) { if (err == qstr("USERNAME_NOT_MODIFIED") || _sentUsername == App::self()->username) {
App::self()->setName(textOneLine(App::self()->firstName), textOneLine(App::self()->lastName), textOneLine(App::self()->nameOrPhone), textOneLine(_sentUsername)); App::self()->setName(TextUtilities::SingleLine(App::self()->firstName), TextUtilities::SingleLine(App::self()->lastName), TextUtilities::SingleLine(App::self()->nameOrPhone), TextUtilities::SingleLine(_sentUsername));
closeBox(); closeBox();
return true; return true;
} else if (err == qstr("USERNAME_INVALID")) { } else if (err == qstr("USERNAME_INVALID")) {

View File

@ -99,7 +99,7 @@ void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool addInli
bool resetScroll = (_type != type || _filter != plainQuery); bool resetScroll = (_type != type || _filter != plainQuery);
if (resetScroll) { if (resetScroll) {
_type = type; _type = type;
_filter = textAccentFold(plainQuery.toString()); _filter = TextUtilities::RemoveAccents(plainQuery.toString());
} }
_addInlineBots = addInlineBots; _addInlineBots = addInlineBots;

View File

@ -336,11 +336,6 @@ enum {
ForwardOnAdd = 100, // how many messages from chat history server should forward to user, that was added to this chat ForwardOnAdd = 100, // how many messages from chat history server should forward to user, that was added to this chat
}; };
inline const QRegularExpression &cWordSplit() {
static QRegularExpression regexp(qsl("[\\@\\s\\-\\+\\(\\)\\[\\]\\{\\}\\<\\>\\,\\.\\:\\!\\_\\;\\\"\\'\\x0]"));
return regexp;
}
inline const QRegularExpression &cRussianLetters() { inline const QRegularExpression &cRussianLetters() {
static QRegularExpression regexp(QString::fromUtf8("[а-яА-ЯёЁ]")); static QRegularExpression regexp(QString::fromUtf8("[а-яА-ЯёЁ]"));
return regexp; return regexp;

View File

@ -40,10 +40,9 @@ Draft::Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled,
void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) {
auto history = App::history(peerId); auto history = App::history(peerId);
auto text = qs(draft.vmessage); auto text = TextWithEntities { qs(draft.vmessage), draft.has_entities() ? TextUtilities::EntitiesFromMTP(draft.ventities.v) : EntitiesInText() };
auto entities = draft.has_entities() ? entitiesFromMTP(draft.ventities.v) : EntitiesInText(); auto textWithTags = TextWithTags { TextUtilities::ApplyEntities(text), ConvertEntitiesToTextTags(text.entities) };
TextWithTags textWithTags = { textApplyEntities(text, entities), ConvertEntitiesToTextTags(entities) }; auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0);
MsgId replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : 0;
auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage()); auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage());
cloudDraft->date = ::date(draft.vdate); cloudDraft->date = ::date(draft.vdate);

View File

@ -99,7 +99,7 @@ void paintRow(Painter &p, const RippleRow *row, History *history, HistoryItem *i
if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) { if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) {
if (history->cloudDraftTextCache.isEmpty()) { if (history->cloudDraftTextCache.isEmpty()) {
auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft))); auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft)));
auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, textClean(draft->textWithTags.text)); auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text));
history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, _textDlgOptions); history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, _textDlgOptions);
} }
p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg));

View File

@ -1263,104 +1263,90 @@ void DialogsInner::onPeerPhotoChanged(PeerData *peer) {
} }
void DialogsInner::onFilterUpdate(QString newFilter, bool force) { void DialogsInner::onFilterUpdate(QString newFilter, bool force) {
newFilter = textSearchKey(newFilter); auto words = TextUtilities::PrepareSearchWords(newFilter);
newFilter = words.isEmpty() ? QString() : words.join(' ');
if (newFilter != _filter || force) { if (newFilter != _filter || force) {
QStringList f; _filter = newFilter;
if (!newFilter.isEmpty()) { if (!_searchInPeer && _filter.isEmpty()) {
QStringList filterList = newFilter.split(cWordSplit(), QString::SkipEmptyParts); _state = DefaultState;
int l = filterList.size(); _hashtagResults.clear();
_filterResults.clear();
_peerSearchResults.clear();
_searchResults.clear();
_lastSearchDate = 0;
_lastSearchPeer = 0;
_lastSearchId = _lastSearchMigratedId = 0;
} else {
QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
f.reserve(l); _state = FilteredState;
for (int i = 0; i < l; ++i) { _filterResults.clear();
QString filterName = filterList[i].trimmed(); if (!_searchInPeer && !words.isEmpty()) {
if (filterName.isEmpty()) continue; const Dialogs::List *toFilter = nullptr;
f.push_back(filterName); if (!_dialogs->isEmpty()) {
} for (fi = fb; fi != fe; ++fi) {
newFilter = f.join(' '); auto found = _dialogs->filtered(fi->at(0));
} if (found->isEmpty()) {
if (newFilter != _filter || force) { toFilter = nullptr;
_filter = newFilter; break;
if (!_searchInPeer && _filter.isEmpty()) { }
_state = DefaultState; if (!toFilter || toFilter->size() > found->size()) {
_hashtagResults.clear(); toFilter = found;
_filterResults.clear();
_peerSearchResults.clear();
_searchResults.clear();
_lastSearchDate = 0;
_lastSearchPeer = 0;
_lastSearchId = _lastSearchMigratedId = 0;
} else {
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
_state = FilteredState;
_filterResults.clear();
if (!_searchInPeer && !f.isEmpty()) {
const Dialogs::List *toFilter = nullptr;
if (!_dialogs->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
auto found = _dialogs->filtered(fi->at(0));
if (found->isEmpty()) {
toFilter = nullptr;
break;
}
if (!toFilter || toFilter->size() > found->size()) {
toFilter = found;
}
} }
} }
const Dialogs::List *toFilterContacts = nullptr; }
if (!_contactsNoDialogs->isEmpty()) { const Dialogs::List *toFilterContacts = nullptr;
for (fi = fb; fi != fe; ++fi) { if (!_contactsNoDialogs->isEmpty()) {
auto found = _contactsNoDialogs->filtered(fi->at(0)); for (fi = fb; fi != fe; ++fi) {
if (found->isEmpty()) { auto found = _contactsNoDialogs->filtered(fi->at(0));
toFilterContacts = nullptr; if (found->isEmpty()) {
break; toFilterContacts = nullptr;
} break;
if (!toFilterContacts || toFilterContacts->size() > found->size()) { }
toFilterContacts = found; if (!toFilterContacts || toFilterContacts->size() > found->size()) {
} toFilterContacts = found;
} }
} }
_filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0)); }
if (toFilter) { _filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0));
for_const (auto row, *toFilter) { if (toFilter) {
const PeerData::Names &names(row->history()->peer->names); for_const (auto row, *toFilter) {
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; const PeerData::Names &names(row->history()->peer->names);
for (fi = fb; fi != fe; ++fi) { PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
QString filterName(*fi); for (fi = fb; fi != fe; ++fi) {
for (ni = nb; ni != ne; ++ni) { QString filterName(*fi);
if (ni->startsWith(*fi)) { for (ni = nb; ni != ne; ++ni) {
break; if (ni->startsWith(*fi)) {
}
}
if (ni == ne) {
break; break;
} }
} }
if (fi == fe) { if (ni == ne) {
_filterResults.push_back(row); break;
} }
} }
if (fi == fe) {
_filterResults.push_back(row);
}
} }
if (toFilterContacts) { }
for_const (auto row, *toFilterContacts) { if (toFilterContacts) {
const PeerData::Names &names(row->history()->peer->names); for_const (auto row, *toFilterContacts) {
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; const PeerData::Names &names(row->history()->peer->names);
for (fi = fb; fi != fe; ++fi) { PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
QString filterName(*fi); for (fi = fb; fi != fe; ++fi) {
for (ni = nb; ni != ne; ++ni) { QString filterName(*fi);
if (ni->startsWith(*fi)) { for (ni = nb; ni != ne; ++ni) {
break; if (ni->startsWith(*fi)) {
}
}
if (ni == ne) {
break; break;
} }
} }
if (fi == fe) { if (ni == ne) {
_filterResults.push_back(row); break;
} }
} }
if (fi == fe) {
_filterResults.push_back(row);
}
} }
} }
} }
@ -2038,10 +2024,10 @@ bool DialogsInner::choosePeer() {
} }
void DialogsInner::saveRecentHashtags(const QString &text) { void DialogsInner::saveRecentHashtags(const QString &text) {
bool found = false; auto found = false;
QRegularExpressionMatch m; QRegularExpressionMatch m;
RecentHashtagPack recent(cRecentSearchHashtags()); auto recent = cRecentSearchHashtags();
for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) { for (int32 i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) {
i = m.capturedStart(); i = m.capturedStart();
next = m.capturedEnd(); next = m.capturedEnd();
if (m.hasMatch()) { if (m.hasMatch()) {

View File

@ -56,11 +56,10 @@ constexpr auto kNewBlockEachMessage = 50;
auto GlobalPinnedIndex = 0; auto GlobalPinnedIndex = 0;
HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from) { HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from) {
QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org"))); auto text = TextWithEntities { lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")) };
EntitiesInText entities; TextUtilities::ParseEntities(text, _historyTextNoMonoOptions.flags);
textParseEntities(text, _historyTextNoMonoOptions.flags, &entities); text.entities.push_front(EntityInText(EntityInTextItalic, 0, text.text.size()));
entities.push_front(EntityInText(EntityInTextItalic, 0, text.size())); return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, text);
return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, { text, entities });
} }
} // namespace } // namespace

View File

@ -392,7 +392,7 @@ void InnerWidget::updateEmptyText() {
auto text = TextWithEntities { lang((hasSearch || hasFilter) ? lng_admin_log_no_results_title : lng_admin_log_no_events_title) }; auto text = TextWithEntities { lang((hasSearch || hasFilter) ? lng_admin_log_no_results_title : lng_admin_log_no_events_title) };
text.entities.append(EntityInText(EntityInTextBold, 0, text.text.size())); text.entities.append(EntityInText(EntityInTextBold, 0, text.text.size()));
auto description = hasSearch auto description = hasSearch
? lng_admin_log_no_results_search_text(lt_query, textClean(_searchQuery)) ? lng_admin_log_no_results_search_text(lt_query, TextUtilities::Clean(_searchQuery))
: lang(hasFilter ? lng_admin_log_no_results_text : lng_admin_log_no_events_text); : lang(hasFilter ? lng_admin_log_no_results_text : lng_admin_log_no_events_text);
text.text.append(qstr("\n\n") + description); text.text.append(qstr("\n\n") + description);
_emptyText.setMarkedText(st::defaultTextStyle, text, options); _emptyText.setMarkedText(st::defaultTextStyle, text, options);

View File

@ -30,14 +30,14 @@ namespace AdminLog {
namespace { namespace {
TextWithEntities PrepareText(const QString &value, const QString &emptyValue) { TextWithEntities PrepareText(const QString &value, const QString &emptyValue) {
auto result = TextWithEntities { textClean(value) }; auto result = TextWithEntities { TextUtilities::Clean(value) };
if (result.text.isEmpty()) { if (result.text.isEmpty()) {
result.text = emptyValue; result.text = emptyValue;
if (!emptyValue.isEmpty()) { if (!emptyValue.isEmpty()) {
result.entities.push_back(EntityInText(EntityInTextItalic, 0, emptyValue.size())); result.entities.push_back(EntityInText(EntityInTextItalic, 0, emptyValue.size()));
} }
} else { } else {
textParseEntities(result.text, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands, &result.entities); TextUtilities::ParseEntities(result, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands);
} }
return result; return result;
} }
@ -79,8 +79,8 @@ TextWithEntities ExtractEditedText(const MTPMessage &message) {
} else if (mediaType == mtpc_messageMediaPhoto) { } else if (mediaType == mtpc_messageMediaPhoto) {
return PrepareText(qs(data.vmedia.c_messageMediaPhoto().vcaption), QString()); return PrepareText(qs(data.vmedia.c_messageMediaPhoto().vcaption), QString());
} }
auto text = textClean(qs(data.vmessage)); auto text = TextUtilities::Clean(qs(data.vmessage));
auto entities = data.has_entities() ? entitiesFromMTP(data.ventities.v) : EntitiesInText(); auto entities = data.has_entities() ? TextUtilities::EntitiesFromMTP(data.ventities.v) : EntitiesInText();
return { text, entities }; return { text, entities };
} }

View File

@ -1522,7 +1522,7 @@ TextWithEntities HistoryInner::getSelectedText() const {
int y = itemTop(item); int y = itemTop(item);
if (y >= 0) { if (y >= 0) {
part.text.append(item->author()->name).append(time); part.text.append(item->author()->name).append(time);
appendTextWithEntities(part, std::move(unwrapped)); TextUtilities::Append(part, std::move(unwrapped));
texts.insert(y, part); texts.insert(y, part);
fullSize += size; fullSize += size;
} }
@ -1532,7 +1532,7 @@ TextWithEntities HistoryInner::getSelectedText() const {
auto sep = qsl("\n\n"); auto sep = qsl("\n\n");
result.text.reserve(fullSize + (texts.size() - 1) * sep.size()); result.text.reserve(fullSize + (texts.size() - 1) * sep.size());
for (auto i = texts.begin(), e = texts.end(); i != e; ++i) { for (auto i = texts.begin(), e = texts.end(); i != e; ++i) {
appendTextWithEntities(result, std::move(i.value())); TextUtilities::Append(result, std::move(i.value()));
if (i + 1 != e) { if (i + 1 != e) {
result.text.append(sep); result.text.append(sep);
} }

View File

@ -115,7 +115,7 @@ ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s)
auto str = row.at(j).text; auto str = row.at(j).text;
button.type = row.at(j).type; button.type = row.at(j).type;
button.link = MakeShared<ReplyMarkupClickHandler>(item, i, j); button.link = MakeShared<ReplyMarkupClickHandler>(item, i, j);
button.text.setText(_st->textStyle(), textOneLine(str), _textPlainOptions); button.text.setText(_st->textStyle(), TextUtilities::SingleLine(str), _textPlainOptions);
button.characters = str.isEmpty() ? 1 : str.size(); button.characters = str.isEmpty() ? 1 : str.size();
} }
_rows.push_back(newRow); _rows.push_back(newRow);
@ -1141,12 +1141,12 @@ QString HistoryItem::inDialogsText() const {
if (emptyText()) { if (emptyText()) {
return _media ? _media->inDialogsText() : QString(); return _media ? _media->inDialogsText() : QString();
} }
return textClean(_text.originalText()); return TextUtilities::Clean(_text.originalText());
}; };
auto plainText = getText(); auto plainText = getText();
if ((!_history->peer->isUser() || out()) && !isPost() && !isEmpty()) { if ((!_history->peer->isUser() || out()) && !isPost() && !isEmpty()) {
auto fromText = author()->isSelf() ? lang(lng_from_you) : author()->shortName(); auto fromText = author()->isSelf() ? lang(lng_from_you) : author()->shortName();
auto fromWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, textClean(fromText))); auto fromWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, TextUtilities::Clean(fromText)));
return lng_dialogs_text_with_from(lt_from_part, fromWrapped, lt_message, plainText); return lng_dialogs_text_with_from(lt_from_part, fromWrapped, lt_message, plainText);
} }
return plainText; return plainText;

View File

@ -42,7 +42,7 @@ public:
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString inDialogsText() const { virtual QString inDialogsText() const {
auto result = notificationText(); auto result = notificationText();
return result.isEmpty() ? QString() : textcmdLink(1, textClean(result)); return result.isEmpty() ? QString() : textcmdLink(1, TextUtilities::Clean(result));
} }
virtual TextWithEntities selectedText(TextSelection selection) const = 0; virtual TextWithEntities selectedText(TextSelection selection) const = 0;

View File

@ -130,7 +130,7 @@ TextWithEntities captionedSelectedText(const QString &attachType, const Text &ca
result.text.append(qstr("[ ")).append(attachType).append(qstr(" ]")); result.text.append(qstr("[ ")).append(attachType).append(qstr(" ]"));
if (!caption.isEmpty()) { if (!caption.isEmpty()) {
result.text.append(qstr("\n")); result.text.append(qstr("\n"));
appendTextWithEntities(result, std::move(original)); TextUtilities::Append(result, std::move(original));
} }
return result; return result;
} }
@ -147,11 +147,11 @@ QString captionedNotificationText(const QString &attachType, const Text &caption
QString captionedInDialogsText(const QString &attachType, const Text &caption) { QString captionedInDialogsText(const QString &attachType, const Text &caption) {
if (caption.isEmpty()) { if (caption.isEmpty()) {
return textcmdLink(1, textClean(attachType)); return textcmdLink(1, TextUtilities::Clean(attachType));
} }
auto captionText = textClean(caption.originalText()); auto captionText = TextUtilities::Clean(caption.originalText());
auto attachTypeWrapped = textcmdLink(1, lng_dialogs_text_media_wrapped(lt_media, textClean(attachType))); auto attachTypeWrapped = textcmdLink(1, lng_dialogs_text_media_wrapped(lt_media, TextUtilities::Clean(attachType)));
return lng_dialogs_text_media(lt_media_part, attachTypeWrapped, lt_caption, captionText); return lng_dialogs_text_media(lt_media_part, attachTypeWrapped, lt_caption, captionText);
} }
@ -3164,7 +3164,7 @@ void HistoryWebPage::initDimensions() {
} }
// init layout // init layout
auto title = textOneLine(_data->title.isEmpty() ? _data->author : _data->title); auto title = TextUtilities::SingleLine(_data->title.isEmpty() ? _data->author : _data->title);
if (!_data->description.text.isEmpty() && title.isEmpty() && _data->siteName.isEmpty() && !_data->url.isEmpty()) { if (!_data->description.text.isEmpty() && title.isEmpty() && _data->siteName.isEmpty() && !_data->url.isEmpty()) {
_data->siteName = siteNameFromUrl(_data->url); _data->siteName = siteNameFromUrl(_data->url);
} }
@ -3642,7 +3642,7 @@ TextWithEntities HistoryWebPage::selectedText(TextSelection selection) const {
} }
titleResult.text += '\n'; titleResult.text += '\n';
appendTextWithEntities(titleResult, std::move(descriptionResult)); TextUtilities::Append(titleResult, std::move(descriptionResult));
return titleResult; return titleResult;
} }
@ -3700,7 +3700,7 @@ void HistoryGame::initDimensions() {
_openl = MakeShared<ReplyMarkupClickHandler>(_parent, 0, 0); _openl = MakeShared<ReplyMarkupClickHandler>(_parent, 0, 0);
} }
auto title = textOneLine(_data->title); auto title = TextUtilities::SingleLine(_data->title);
// init attach // init attach
if (!_attach) { if (!_attach) {
@ -3728,7 +3728,7 @@ void HistoryGame::initDimensions() {
} }
auto marked = TextWithEntities { text }; auto marked = TextWithEntities { text };
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
textParseEntities(marked.text, parseFlags, &marked.entities); TextUtilities::ParseEntities(marked, parseFlags);
_description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions); _description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions);
} }
} }
@ -4042,7 +4042,7 @@ TextWithEntities HistoryGame::selectedText(TextSelection selection) const {
} }
titleResult.text += '\n'; titleResult.text += '\n';
appendTextWithEntities(titleResult, std::move(descriptionResult)); TextUtilities::Append(titleResult, std::move(descriptionResult));
return titleResult; return titleResult;
} }
@ -4156,10 +4156,10 @@ void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) {
if (!description.isEmpty()) { if (!description.isEmpty()) {
auto marked = TextWithEntities { description }; auto marked = TextWithEntities { description };
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
textParseEntities(marked.text, parseFlags, &marked.entities); TextUtilities::ParseEntities(marked, parseFlags);
_description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions); _description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions);
} }
auto title = textOneLine(qs(data.vtitle)); auto title = TextUtilities::SingleLine(qs(data.vtitle));
if (!title.isEmpty()) { if (!title.isEmpty()) {
_title.setText(st::webPageTitleStyle, title, _webpageTitleOptions); _title.setText(st::webPageTitleStyle, title, _webpageTitleOptions);
} }
@ -4435,7 +4435,7 @@ TextWithEntities HistoryInvoice::selectedText(TextSelection selection) const {
} }
titleResult.text += '\n'; titleResult.text += '\n';
appendTextWithEntities(titleResult, std::move(descriptionResult)); TextUtilities::Append(titleResult, std::move(descriptionResult));
return titleResult; return titleResult;
} }
@ -4460,12 +4460,12 @@ HistoryLocation::HistoryLocation(gsl::not_null<HistoryItem*> parent, const Locat
, _description(st::msgMinWidth) , _description(st::msgMinWidth)
, _link(MakeShared<LocationClickHandler>(coords)) { , _link(MakeShared<LocationClickHandler>(coords)) {
if (!title.isEmpty()) { if (!title.isEmpty()) {
_title.setText(st::webPageTitleStyle, textClean(title), _webpageTitleOptions); _title.setText(st::webPageTitleStyle, TextUtilities::Clean(title), _webpageTitleOptions);
} }
if (!description.isEmpty()) { if (!description.isEmpty()) {
auto marked = TextWithEntities { textClean(description) }; auto marked = TextWithEntities { TextUtilities::Clean(description) };
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
textParseEntities(marked.text, parseFlags, &marked.entities); TextUtilities::ParseEntities(marked, parseFlags);
_description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions); _description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions);
} }
} }
@ -4700,7 +4700,7 @@ TextWithEntities HistoryLocation::selectedText(TextSelection selection) const {
TextWithEntities result = { qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"), EntitiesInText() }; TextWithEntities result = { qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"), EntitiesInText() };
auto info = selectedText(AllTextSelection); auto info = selectedText(AllTextSelection);
if (!info.text.isEmpty()) { if (!info.text.isEmpty()) {
appendTextWithEntities(result, std::move(info)); TextUtilities::Append(result, std::move(info));
result.text.append('\n'); result.text.append('\n');
} }
result.text += _link->dragText(); result.text += _link->dragText();
@ -4715,7 +4715,7 @@ TextWithEntities HistoryLocation::selectedText(TextSelection selection) const {
return titleResult; return titleResult;
} }
titleResult.text += '\n'; titleResult.text += '\n';
appendTextWithEntities(titleResult, std::move(descriptionResult)); TextUtilities::Append(titleResult, std::move(descriptionResult));
return titleResult; return titleResult;
} }

View File

@ -221,7 +221,7 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) {
} }
if (replyToMsg) { if (replyToMsg) {
replyToText.setText(st::messageTextStyle, textClean(replyToMsg->inReplyText()), _textDlgOptions); replyToText.setText(st::messageTextStyle, TextUtilities::Clean(replyToMsg->inReplyText()), _textDlgOptions);
updateName(); updateName();
@ -432,8 +432,8 @@ HistoryMessage::HistoryMessage(gsl::not_null<History*> history, const MTPDmessag
initMedia(msg.has_media() ? (&msg.vmedia) : nullptr); initMedia(msg.has_media() ? (&msg.vmedia) : nullptr);
auto text = textClean(qs(msg.vmessage)); auto text = TextUtilities::Clean(qs(msg.vmessage));
auto entities = msg.has_entities() ? entitiesFromMTP(msg.ventities.v) : EntitiesInText(); auto entities = msg.has_entities() ? TextUtilities::EntitiesFromMTP(msg.ventities.v) : EntitiesInText();
setText({ text, entities }); setText({ text, entities });
} }
@ -980,7 +980,7 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() }; TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() };
if (message.has_entities()) { if (message.has_entities()) {
textWithEntities.entities = entitiesFromMTP(message.ventities.v); textWithEntities.entities = TextUtilities::EntitiesFromMTP(message.ventities.v);
} }
setText(textWithEntities); setText(textWithEntities);
setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr); setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr);
@ -1075,13 +1075,13 @@ TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
result = std::move(mediaResult); result = std::move(mediaResult);
} else if (!mediaResult.text.isEmpty()) { } else if (!mediaResult.text.isEmpty()) {
result.text += qstr("\n\n"); result.text += qstr("\n\n");
appendTextWithEntities(result, std::move(mediaResult)); TextUtilities::Append(result, std::move(mediaResult));
} }
if (result.text.isEmpty()) { if (result.text.isEmpty()) {
result = std::move(logEntryOriginalResult); result = std::move(logEntryOriginalResult);
} else if (!logEntryOriginalResult.text.isEmpty()) { } else if (!logEntryOriginalResult.text.isEmpty()) {
result.text += qstr("\n\n"); result.text += qstr("\n\n");
appendTextWithEntities(result, std::move(logEntryOriginalResult)); TextUtilities::Append(result, std::move(logEntryOriginalResult));
} }
if (auto forwarded = Get<HistoryMessageForwarded>()) { if (auto forwarded = Get<HistoryMessageForwarded>()) {
if (selection == FullSelection) { if (selection == FullSelection) {
@ -1090,9 +1090,9 @@ TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size()); wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size());
wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size()); wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size());
wrapped.text.append('['); wrapped.text.append('[');
appendTextWithEntities(wrapped, std::move(fwdinfo)); TextUtilities::Append(wrapped, std::move(fwdinfo));
wrapped.text.append(qsl("]\n")); wrapped.text.append(qsl("]\n"));
appendTextWithEntities(wrapped, std::move(result)); TextUtilities::Append(wrapped, std::move(result));
result = wrapped; result = wrapped;
} }
} }
@ -1101,7 +1101,7 @@ TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
TextWithEntities wrapped; TextWithEntities wrapped;
wrapped.text.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.text.size()); 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")); wrapped.text.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n"));
appendTextWithEntities(wrapped, std::move(result)); TextUtilities::Append(wrapped, std::move(result));
result = wrapped; result = wrapped;
} }
} }

View File

@ -89,7 +89,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto prepareChatCreate = [this](const MTPDmessageActionChatCreate &action) { auto prepareChatCreate = [this](const MTPDmessageActionChatCreate &action) {
auto result = PreparedText {}; auto result = PreparedText {};
result.links.push_back(fromLink()); result.links.push_back(fromLink());
result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, textClean(qs(action.vtitle))); result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle)));
return result; return result;
}; };
@ -99,7 +99,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
result.text = lang(lng_action_created_channel); result.text = lang(lng_action_created_channel);
} else { } else {
result.links.push_back(fromLink()); result.links.push_back(fromLink());
result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, textClean(qs(action.vtitle))); result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle)));
} }
return result; return result;
}; };
@ -143,10 +143,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto prepareChatEditTitle = [this](const MTPDmessageActionChatEditTitle &action) { auto prepareChatEditTitle = [this](const MTPDmessageActionChatEditTitle &action) {
auto result = PreparedText {}; auto result = PreparedText {};
if (isPost()) { if (isPost()) {
result.text = lng_action_changed_title_channel(lt_title, textClean(qs(action.vtitle))); result.text = lng_action_changed_title_channel(lt_title, TextUtilities::Clean(qs(action.vtitle)));
} else { } else {
result.links.push_back(fromLink()); result.links.push_back(fromLink());
result.text = lng_action_changed_title(lt_from, fromLinkText(), lt_title, textClean(qs(action.vtitle))); result.text = lng_action_changed_title(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle)));
} }
return result; return result;
}; };
@ -423,7 +423,7 @@ TextWithEntities HistoryService::selectedText(TextSelection selection) const {
} }
QString HistoryService::inDialogsText() const { QString HistoryService::inDialogsText() const {
return textcmdLink(1, textClean(notificationText())); return textcmdLink(1, TextUtilities::Clean(notificationText()));
} }
QString HistoryService::inReplyText() const { QString HistoryService::inReplyText() const {

View File

@ -2873,14 +2873,15 @@ void HistoryWidget::saveEditMsg() {
auto &textWithTags = _field->getTextWithTags(); auto &textWithTags = _field->getTextWithTags();
auto prepareFlags = itemTextOptions(_history, App::self()).flags; auto prepareFlags = itemTextOptions(_history, App::self()).flags;
EntitiesInText sendingEntities, leftEntities = ConvertTextTagsToEntities(textWithTags.tags); auto sending = TextWithEntities();
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities); auto left = TextWithEntities { textWithTags.text, ConvertTextTagsToEntities(textWithTags.tags) };
TextUtilities::PrepareForSending(left, prepareFlags);
if (!textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { if (!TextUtilities::CutPart(sending, left, MaxMessageSize)) {
_field->selectAll(); _field->selectAll();
_field->setFocus(); _field->setFocus();
return; return;
} else if (!leftText.isEmpty()) { } else if (!left.text.isEmpty()) {
Ui::show(Box<InformBox>(lang(lng_edit_too_long))); Ui::show(Box<InformBox>(lang(lng_edit_too_long)));
return; return;
} }
@ -2889,12 +2890,12 @@ void HistoryWidget::saveEditMsg() {
if (webPageId == CancelledWebPageId) { if (webPageId == CancelledWebPageId) {
sendFlags |= MTPmessages_EditMessage::Flag::f_no_webpage; sendFlags |= MTPmessages_EditMessage::Flag::f_no_webpage;
} }
auto localEntities = linksToMTP(sendingEntities); auto localEntities = TextUtilities::EntitiesToMTP(sending.entities);
auto sentEntities = linksToMTP(sendingEntities, true); auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) { if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_EditMessage::Flag::f_entities; sendFlags |= MTPmessages_EditMessage::Flag::f_entities;
} }
_saveEditMsgRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(sendFlags), _history->peer->input, MTP_int(_editMsgId), MTP_string(sendingText), MTPnullMarkup, sentEntities), rpcDone(&HistoryWidget::saveEditMsgDone, _history), rpcFail(&HistoryWidget::saveEditMsgFail, _history)); _saveEditMsgRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(sendFlags), _history->peer->input, MTP_int(_editMsgId), MTP_string(sending.text), MTPnullMarkup, sentEntities), rpcDone(&HistoryWidget::saveEditMsgDone, _history), rpcFail(&HistoryWidget::saveEditMsgFail, _history));
} }
void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates, mtpRequestId req) { void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates, mtpRequestId req) {
@ -3841,7 +3842,7 @@ void HistoryWidget::onKbToggle(bool manual) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0;
if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) {
updateReplyToName(); updateReplyToName();
_replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions);
_fieldBarCancel->show(); _fieldBarCancel->show();
updateMouseTracking(); updateMouseTracking();
} }
@ -3860,7 +3861,7 @@ void HistoryWidget::onKbToggle(bool manual) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0;
if (_kbReplyTo && !_editMsgId && !_replyToId) { if (_kbReplyTo && !_editMsgId && !_replyToId) {
updateReplyToName(); updateReplyToName();
_replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions);
_fieldBarCancel->show(); _fieldBarCancel->show();
updateMouseTracking(); updateMouseTracking();
} }
@ -5181,7 +5182,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0;
if (_kbReplyTo && !_replyToId) { if (_kbReplyTo && !_replyToId) {
updateReplyToName(); updateReplyToName();
_replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions);
_fieldBarCancel->show(); _fieldBarCancel->show();
updateMouseTracking(); updateMouseTracking();
} }
@ -5440,7 +5441,7 @@ void HistoryWidget::updatePinnedBar(bool force) {
_pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId); _pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId);
} }
if (_pinnedBar->msg) { if (_pinnedBar->msg) {
_pinnedBar->text.setText(st::messageTextStyle, textClean(_pinnedBar->msg->notificationText()), _textDlgOptions); _pinnedBar->text.setText(st::messageTextStyle, TextUtilities::Clean(_pinnedBar->msg->notificationText()), _textDlgOptions);
update(); update();
} else if (force) { } else if (force) {
if (_peer && _peer->isMegagroup()) { if (_peer && _peer->isMegagroup()) {
@ -5667,7 +5668,7 @@ void HistoryWidget::onReplyToMessage() {
} else { } else {
_replyEditMsg = to; _replyEditMsg = to;
_replyToId = to->id; _replyToId = to->id;
_replyEditMsgText.setText(st::messageTextStyle, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_replyEditMsg->inReplyText()), _textDlgOptions);
updateBotKeyboard(); updateBotKeyboard();
@ -5709,10 +5710,8 @@ void HistoryWidget::onEditMessage() {
} }
auto original = to->originalText(); auto original = to->originalText();
auto editText = textApplyEntities(original.text, original.entities); auto editData = TextWithTags { TextUtilities::ApplyEntities(original), ConvertEntitiesToTextTags(original.entities) };
auto editTags = ConvertEntitiesToTextTags(original.entities); auto cursor = MessageCursor { editData.text.size(), editData.text.size(), QFIXED_MAX };
TextWithTags editData = { editText, editTags };
MessageCursor cursor = { editText.size(), editText.size(), QFIXED_MAX };
_history->setEditDraft(std::make_unique<Data::Draft>(editData, to->id, cursor, false)); _history->setEditDraft(std::make_unique<Data::Draft>(editData, to->id, cursor, false));
applyDraft(false); applyDraft(false);
@ -6013,7 +6012,7 @@ void HistoryWidget::updatePreview() {
#else // OS_MAC_OLD #else // OS_MAC_OLD
auto linkText = _previewLinks.split(' ').at(0); auto linkText = _previewLinks.split(' ').at(0);
#endif // OS_MAC_OLD #endif // OS_MAC_OLD
_previewDescription.setText(st::messageTextStyle, textClean(linkText), _textDlgOptions); _previewDescription.setText(st::messageTextStyle, TextUtilities::Clean(linkText), _textDlgOptions);
int32 t = (_previewData->pendingTill - unixtime()) * 1000; int32 t = (_previewData->pendingTill - unixtime()) * 1000;
if (t <= 0) t = 1; if (t <= 0) t = 1;
@ -6045,7 +6044,7 @@ void HistoryWidget::updatePreview() {
} }
} }
_previewTitle.setText(st::msgNameStyle, title, _textNameOptions); _previewTitle.setText(st::msgNameStyle, title, _textNameOptions);
_previewDescription.setText(st::messageTextStyle, textClean(desc), _textDlgOptions); _previewDescription.setText(st::messageTextStyle, TextUtilities::Clean(desc), _textDlgOptions);
} }
} else if (!readyToForward() && !replyToId() && !_editMsgId) { } else if (!readyToForward() && !replyToId() && !_editMsgId) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
@ -6060,9 +6059,7 @@ void HistoryWidget::onCancel() {
onInlineBotCancel(); onInlineBotCancel();
} else if (_editMsgId) { } else if (_editMsgId) {
auto original = _replyEditMsg ? _replyEditMsg->originalText() : TextWithEntities(); auto original = _replyEditMsg ? _replyEditMsg->originalText() : TextWithEntities();
auto editText = textApplyEntities(original.text, original.entities); auto editData = TextWithTags { TextUtilities::ApplyEntities(original), ConvertEntitiesToTextTags(original.entities) };
auto editTags = ConvertEntitiesToTextTags(original.entities);
TextWithTags editData = { editText, editTags };
if (_replyEditMsg && editData != _field->getTextWithTags()) { if (_replyEditMsg && editData != _field->getTextWithTags()) {
Ui::show(Box<ConfirmBox>( Ui::show(Box<ConfirmBox>(
lang(lng_cancel_edit_post_sure), lang(lng_cancel_edit_post_sure),
@ -6329,7 +6326,7 @@ void HistoryWidget::updateReplyEditTexts(bool force) {
_replyEditMsg = App::histItemById(_channel, _editMsgId ? _editMsgId : _replyToId); _replyEditMsg = App::histItemById(_channel, _editMsgId ? _editMsgId : _replyToId);
} }
if (_replyEditMsg) { if (_replyEditMsg) {
_replyEditMsgText.setText(st::messageTextStyle, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_replyEditMsg->inReplyText()), _textDlgOptions);
updateBotKeyboard(); updateBotKeyboard();
@ -6390,7 +6387,7 @@ void HistoryWidget::updateForwardingTexts() {
} }
} }
_toForwardFrom.setText(st::msgNameStyle, from, _textNameOptions); _toForwardFrom.setText(st::msgNameStyle, from, _textNameOptions);
_toForwardText.setText(st::messageTextStyle, textClean(text), _textDlgOptions); _toForwardText.setText(st::messageTextStyle, TextUtilities::Clean(text), _textDlgOptions);
_toForwardNameVersion = version; _toForwardNameVersion = version;
} }

View File

@ -570,7 +570,7 @@ void Video::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
QString title = textOneLine(_result->getLayoutTitle()); auto title = TextUtilities::SingleLine(_result->getLayoutTitle());
if (title.isEmpty()) { if (title.isEmpty()) {
title = lang(lng_media_video); title = lang(lng_media_video);
} }
@ -685,7 +685,7 @@ void File::initDimensions() {
int textWidth = _maxw - (st::msgFileSize + st::inlineThumbSkip); int textWidth = _maxw - (st::msgFileSize + st::inlineThumbSkip);
TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts);
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto };
_description.setText(st::defaultTextStyle, _result->getLayoutDescription(), descriptionOpts); _description.setText(st::defaultTextStyle, _result->getLayoutDescription(), descriptionOpts);
@ -890,7 +890,7 @@ void Contact::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip);
TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), st::semiboldFont->height); int32 titleHeight = qMin(_title.countHeight(_maxw), st::semiboldFont->height);
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto };
@ -987,7 +987,7 @@ void Article::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height);
int32 descriptionLines = (_withThumb || _url) ? 2 : 3; int32 descriptionLines = (_withThumb || _url) ? 2 : 3;
@ -1155,7 +1155,7 @@ void Game::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height);
int32 descriptionLines = 2; int32 descriptionLines = 2;

View File

@ -136,7 +136,7 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
case mtpc_botInlineMessageText: { case mtpc_botInlineMessageText: {
auto &r = message->c_botInlineMessageText(); auto &r = message->c_botInlineMessageText();
auto entities = r.has_entities() ? entitiesFromMTP(r.ventities.v) : EntitiesInText(); auto entities = r.has_entities() ? TextUtilities::EntitiesFromMTP(r.ventities.v) : EntitiesInText();
result->sendData = std::make_unique<internal::SendText>(qs(r.vmessage), entities, r.is_no_webpage()); result->sendData = std::make_unique<internal::SendText>(qs(r.vmessage), entities, r.is_no_webpage());
if (result->_type == Type::Photo) { if (result->_type == Type::Photo) {
result->createPhoto(); result->createPhoto();

View File

@ -57,7 +57,7 @@ QString SendDataCommon::getErrorOnSend(const Result *owner, History *history) co
SendDataCommon::SentMTPMessageFields SendText::getSentMessageFields() const { SendDataCommon::SentMTPMessageFields SendText::getSentMessageFields() const {
SentMTPMessageFields result; SentMTPMessageFields result;
result.text = MTP_string(_message); result.text = MTP_string(_message);
result.entities = linksToMTP(_entities); result.entities = TextUtilities::EntitiesToMTP(_entities);
return result; return result;
} }

View File

@ -1479,23 +1479,24 @@ void MainWidget::sendMessage(const MessageToSend &message) {
} }
saveRecentHashtags(textWithTags.text); saveRecentHashtags(textWithTags.text);
EntitiesInText sendingEntities, leftEntities = ConvertTextTagsToEntities(textWithTags.tags); auto sending = TextWithEntities();
auto left = TextWithEntities { textWithTags.text, ConvertTextTagsToEntities(textWithTags.tags) };
auto prepareFlags = itemTextOptions(history, App::self()).flags; auto prepareFlags = itemTextOptions(history, App::self()).flags;
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities); TextUtilities::PrepareForSending(left, prepareFlags);
HistoryItem *lastMessage = nullptr; HistoryItem *lastMessage = nullptr;
MsgId replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo; auto replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo;
while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
FullMsgId newId(peerToChannel(history->peer->id), clientMsgId()); auto newId = FullMsgId(peerToChannel(history->peer->id), clientMsgId());
uint64 randomId = rand_value<uint64>(); auto randomId = rand_value<uint64>();
trimTextWithEntities(sendingText, &sendingEntities); TextUtilities::Trim(sending);
App::historyRegRandom(randomId, newId); App::historyRegRandom(randomId, newId);
App::historyRegSentData(randomId, history->peer->id, sendingText); App::historyRegSentData(randomId, history->peer->id, sending.text);
MTPstring msgText(MTP_string(sendingText)); MTPstring msgText(MTP_string(sending.text));
auto flags = NewMessageFlags(history->peer) | MTPDmessage::Flag::f_entities; // unread, out auto flags = NewMessageFlags(history->peer) | MTPDmessage::Flag::f_entities; // unread, out
auto sendFlags = MTPmessages_SendMessage::Flags(0); auto sendFlags = MTPmessages_SendMessage::Flags(0);
if (replyTo) { if (replyTo) {
@ -1523,8 +1524,8 @@ void MainWidget::sendMessage(const MessageToSend &message) {
if (silentPost) { if (silentPost) {
sendFlags |= MTPmessages_SendMessage::Flag::f_silent; sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
} }
auto localEntities = linksToMTP(sendingEntities); auto localEntities = TextUtilities::EntitiesToMTP(sending.entities);
auto sentEntities = linksToMTP(sendingEntities, true); auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) { if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMessage::Flag::f_entities; sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
} }
@ -1546,7 +1547,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
bool found = false; bool found = false;
QRegularExpressionMatch m; QRegularExpressionMatch m;
RecentHashtagPack recent(cRecentWriteHashtags()); RecentHashtagPack recent(cRecentWriteHashtags());
for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) { for (int32 i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) {
i = m.capturedStart(); i = m.capturedStart();
next = m.capturedEnd(); next = m.capturedEnd();
if (m.hasMatch()) { if (m.hasMatch()) {
@ -2093,12 +2094,11 @@ void MainWidget::dialogsCancelled() {
void MainWidget::insertCheckedServiceNotification(const TextWithEntities &message, const MTPMessageMedia &media, int32 date) { void MainWidget::insertCheckedServiceNotification(const TextWithEntities &message, const MTPMessageMedia &media, int32 date) {
auto flags = MTPDmessage::Flag::f_entities | MTPDmessage::Flag::f_from_id | MTPDmessage_ClientFlag::f_clientside_unread; auto flags = MTPDmessage::Flag::f_entities | MTPDmessage::Flag::f_from_id | MTPDmessage_ClientFlag::f_clientside_unread;
QString sendingText, leftText = message.text; auto sending = TextWithEntities(), left = message;
EntitiesInText sendingEntities, leftEntities = message.entities;
HistoryItem *item = nullptr; HistoryItem *item = nullptr;
while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
MTPVector<MTPMessageEntity> localEntities = linksToMTP(sendingEntities); auto localEntities = TextUtilities::EntitiesToMTP(sending.entities);
item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(AuthSession::CurrentUserId())), MTPnullFwdHeader, MTPint(), MTPint(), MTP_int(date), MTP_string(sendingText), media, MTPnullMarkup, localEntities, MTPint(), MTPint()), NewMessageUnread); item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(AuthSession::CurrentUserId())), MTPnullFwdHeader, MTPint(), MTPint(), MTP_int(date), MTP_string(sending.text), media, MTPnullMarkup, localEntities, MTPint(), MTPint()), NewMessageUnread);
} }
if (item) { if (item) {
_history->peerMessagesUpdated(item->history()->peer->id); _history->peerMessagesUpdated(item->history()->peer->id);
@ -4878,7 +4878,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
if (d.has_entities() && !mentionUsersLoaded(d.ventities)) { if (d.has_entities() && !mentionUsersLoaded(d.ventities)) {
AuthSession::Current().api().requestMessageData(item->history()->peer->asChannel(), item->id, ApiWrap::RequestMessageDataCallback()); AuthSession::Current().api().requestMessageData(item->history()->peer->asChannel(), item->id, ApiWrap::RequestMessageDataCallback());
} }
auto entities = d.has_entities() ? entitiesFromMTP(d.ventities.v) : EntitiesInText(); auto entities = d.has_entities() ? TextUtilities::EntitiesFromMTP(d.ventities.v) : EntitiesInText();
item->setText({ text, entities }); item->setText({ text, entities });
item->updateMedia(d.has_media() ? (&d.vmedia) : nullptr); item->updateMedia(d.has_media() ? (&d.vmedia) : nullptr);
item->addToOverview(AddToOverviewNew); item->addToOverview(AddToOverviewNew);
@ -5129,9 +5129,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
auto &d = update.c_updateUserName(); auto &d = update.c_updateUserName();
if (auto user = App::userLoaded(d.vuser_id.v)) { if (auto user = App::userLoaded(d.vuser_id.v)) {
if (user->contact <= 0) { if (user->contact <= 0) {
user->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), user->nameOrPhone, textOneLine(qs(d.vusername))); user->setName(TextUtilities::SingleLine(qs(d.vfirst_name)), TextUtilities::SingleLine(qs(d.vlast_name)), user->nameOrPhone, TextUtilities::SingleLine(qs(d.vusername)));
} else { } else {
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), user->nameOrPhone, textOneLine(qs(d.vusername))); user->setName(TextUtilities::SingleLine(user->firstName), TextUtilities::SingleLine(user->lastName), user->nameOrPhone, TextUtilities::SingleLine(qs(d.vusername)));
} }
App::markPeerUpdated(user); App::markPeerUpdated(user);
} }
@ -5234,7 +5234,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
if (d.is_popup()) { if (d.is_popup()) {
Ui::show(Box<InformBox>(qs(d.vmessage))); Ui::show(Box<InformBox>(qs(d.vmessage)));
} else { } else {
App::wnd()->serviceNotification({ qs(d.vmessage), entitiesFromMTP(d.ventities.v) }, d.vmedia); App::wnd()->serviceNotification({ qs(d.vmessage), TextUtilities::EntitiesFromMTP(d.ventities.v) }, d.vmedia);
emit App::wnd()->checkNewAuthorization(); emit App::wnd()->checkNewAuthorization();
} }
} break; } break;

View File

@ -323,7 +323,7 @@ void CoverWidget::handleSongChange() {
if (song->performer.isEmpty()) { if (song->performer.isEmpty()) {
textWithEntities.text = song->title.isEmpty() ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title; textWithEntities.text = song->title.isEmpty() ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title;
} else { } else {
auto title = song->title.isEmpty() ? qsl("Unknown Track") : textClean(song->title); auto title = song->title.isEmpty() ? qsl("Unknown Track") : TextUtilities::Clean(song->title);
textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title; textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title;
textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() }); textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() });
} }

View File

@ -500,7 +500,7 @@ void Widget::handleSongChange() {
if (!song || song->performer.isEmpty()) { if (!song || song->performer.isEmpty()) {
textWithEntities.text = (!song || song->title.isEmpty()) ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title; textWithEntities.text = (!song || song->title.isEmpty()) ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title;
} else { } else {
auto title = song->title.isEmpty() ? qsl("Unknown Track") : textClean(song->title); auto title = song->title.isEmpty() ? qsl("Unknown Track") : TextUtilities::Clean(song->title);
textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title; textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title;
textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() }); textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() });
} }

View File

@ -506,7 +506,7 @@ Voice::Voice(DocumentData *voice, HistoryItem *parent, const style::OverviewFile
setDocumentLinks(_data); setDocumentLinks(_data);
updateName(); updateName();
QString d = textcmdLink(1, textRichPrepare(langDateTime(date(_data->date)))); QString d = textcmdLink(1, TextUtilities::EscapeForRichParsing(langDateTime(date(_data->date))));
TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto }; TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto };
_details.setText(st::defaultTextStyle, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts); _details.setText(st::defaultTextStyle, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts);
_details.setLink(1, goToMessageClickHandler(parent)); _details.setLink(1, goToMessageClickHandler(parent));

View File

@ -309,9 +309,9 @@ void GroupMembersWidget::refreshLimitReached() {
bool limitReachedShown = (itemsCount() >= Global::ChatSizeMax()) && chat->amCreator() && !emptyTitle(); bool limitReachedShown = (itemsCount() >= Global::ChatSizeMax()) && chat->amCreator() && !emptyTitle();
if (limitReachedShown && !_limitReachedInfo) { if (limitReachedShown && !_limitReachedInfo) {
_limitReachedInfo.create(this, st::profileLimitReachedLabel); _limitReachedInfo.create(this, st::profileLimitReachedLabel);
QString title = textRichPrepare(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax())); QString title = TextUtilities::EscapeForRichParsing(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax()));
QString body = textRichPrepare(lang(lng_profile_migrate_body)); QString body = TextUtilities::EscapeForRichParsing(lang(lng_profile_migrate_body));
QString link = textRichPrepare(lang(lng_profile_migrate_learn_more)); QString link = TextUtilities::EscapeForRichParsing(lang(lng_profile_migrate_learn_more));
QString text = qsl("%1%2%3\n%4 [a href=\"https://telegram.org/blog/supergroups5k\"]%5[/a]").arg(textcmdStartSemibold()).arg(title).arg(textcmdStopSemibold()).arg(body).arg(link); QString text = qsl("%1%2%3\n%4 [a href=\"https://telegram.org/blog/supergroups5k\"]%5[/a]").arg(textcmdStartSemibold()).arg(title).arg(textcmdStopSemibold()).arg(body).arg(link);
_limitReachedInfo->setRichText(text); _limitReachedInfo->setRichText(text);
_limitReachedInfo->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) { _limitReachedInfo->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {

View File

@ -149,12 +149,12 @@ void InfoWidget::refreshAbout() {
}; };
_about.destroy(); _about.destroy();
auto aboutText = TextWithEntities { textClean(getAboutText()) }; auto aboutText = TextWithEntities { TextUtilities::Clean(getAboutText()) };
if (!aboutText.text.isEmpty()) { if (!aboutText.text.isEmpty()) {
_about.create(this, st::profileBlockTextPart); _about.create(this, st::profileBlockTextPart);
_about->show(); _about->show();
textParseEntities(aboutText.text, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands, &aboutText.entities); TextUtilities::ParseEntities(aboutText, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands);
_about->setMarkedText(aboutText); _about->setMarkedText(aboutText);
_about->setSelectable(true); _about->setSelectable(true);
_about->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) { _about->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {

View File

@ -1120,16 +1120,7 @@ void AddParticipantBoxSearchController::searchGlobalDone(mtpRequestId requestId,
void AddParticipantBoxSearchController::addChatsContacts() { void AddParticipantBoxSearchController::addChatsContacts() {
_chatsContactsAdded = true; _chatsContactsAdded = true;
auto filterWordList = _query.split(cWordSplit(), QString::SkipEmptyParts); auto wordList = TextUtilities::PrepareSearchWords(_query);
auto wordsCount = filterWordList.size();
auto wordList = QStringList();
wordList.reserve(wordsCount);
for_const (auto &word, filterWordList) {
auto trimmed = word.trimmed();
if (!trimmed.isEmpty()) {
wordList.push_back(trimmed);
}
}
if (wordList.empty()) { if (wordList.empty()) {
return; return;
} }

View File

@ -461,22 +461,22 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { // see Local::readPeer a
void PeerData::fillNames() { void PeerData::fillNames() {
names.clear(); names.clear();
chars.clear(); chars.clear();
QString toIndex = textAccentFold(name); auto toIndex = TextUtilities::RemoveAccents(name);
if (cRussianLetters().match(toIndex).hasMatch()) { if (cRussianLetters().match(toIndex).hasMatch()) {
toIndex += ' ' + translitRusEng(toIndex); toIndex += ' ' + translitRusEng(toIndex);
} }
if (isUser()) { if (isUser()) {
if (!asUser()->nameOrPhone.isEmpty() && asUser()->nameOrPhone != name) toIndex += ' ' + textAccentFold(asUser()->nameOrPhone); if (!asUser()->nameOrPhone.isEmpty() && asUser()->nameOrPhone != name) toIndex += ' ' + TextUtilities::RemoveAccents(asUser()->nameOrPhone);
if (!asUser()->username.isEmpty()) toIndex += ' ' + textAccentFold(asUser()->username); if (!asUser()->username.isEmpty()) toIndex += ' ' + TextUtilities::RemoveAccents(asUser()->username);
} else if (isChannel()) { } else if (isChannel()) {
if (!asChannel()->username.isEmpty()) toIndex += ' ' + textAccentFold(asChannel()->username); if (!asChannel()->username.isEmpty()) toIndex += ' ' + TextUtilities::RemoveAccents(asChannel()->username);
} }
toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex); toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex);
QStringList namesList = toIndex.toLower().split(cWordSplit(), QString::SkipEmptyParts); auto namesList = TextUtilities::PrepareSearchWords(toIndex);
for (QStringList::const_iterator i = namesList.cbegin(), e = namesList.cend(); i != e; ++i) { for (auto &name : namesList) {
names.insert(*i); names.insert(name);
chars.insert(i->at(0)); chars.insert(name[0]);
} }
} }

View File

@ -417,21 +417,8 @@ void CountrySelectBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
} }
void CountrySelectBox::Inner::updateFilter(QString filter) { void CountrySelectBox::Inner::updateFilter(QString filter) {
filter = textSearchKey(filter); auto words = TextUtilities::PrepareSearchWords(filter);
filter = words.isEmpty() ? QString() : words.join(' ');
QStringList f;
if (!filter.isEmpty()) {
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
if (_filter != filter) { if (_filter != filter) {
_filter = filter; _filter = filter;
@ -441,7 +428,7 @@ void CountrySelectBox::Inner::updateFilter(QString filter) {
QChar first = _filter[0].toLower(); QChar first = _filter[0].toLower();
CountriesIds &ids(countriesByLetter[first]); CountriesIds &ids(countriesByLetter[first]);
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
countriesFiltered.clear(); countriesFiltered.clear();
for (CountriesIds::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) { for (CountriesIds::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {

View File

@ -197,29 +197,30 @@ inline QString Filename(int index = Index()) {
return QString::fromLatin1(EmojiNames[index]); return QString::fromLatin1(EmojiNames[index]);
} }
inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText *inOutEntities) { inline void AppendPartToResult(TextWithEntities &result, const QChar *start, const QChar *from, const QChar *to) {
if (to > from) { if (to > from) {
for (auto &entity : *inOutEntities) { for (auto &entity : result.entities) {
if (entity.offset() >= to - start) break; if (entity.offset() >= to - start) break;
if (entity.offset() + entity.length() < from - start) continue; if (entity.offset() + entity.length() < from - start) continue;
if (entity.offset() >= from - start) { if (entity.offset() >= from - start) {
entity.extendToLeft(from - start - result.size()); entity.extendToLeft(from - start - result.text.size());
} }
if (entity.offset() + entity.length() <= to - start) { if (entity.offset() + entity.length() <= to - start) {
entity.shrinkFromRight(from - start - result.size()); entity.shrinkFromRight(from - start - result.text.size());
} }
} }
result.append(from, to - from); result.text.append(from, to - from);
} }
} }
inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities) { inline void ReplaceInText(TextWithEntities &result) {
auto result = QString(); auto newText = TextWithEntities();
auto currentEntity = inOutEntities->begin(); newText.entities = std::move(result.entities);
auto entitiesEnd = inOutEntities->end(); auto currentEntity = newText.entities.begin();
auto emojiStart = text.constData(); auto entitiesEnd = newText.entities.end();
auto emojiStart = result.text.constData();
auto emojiEnd = emojiStart; auto emojiEnd = emojiStart;
auto end = emojiStart + text.size(); auto end = emojiStart + result.text.size();
auto canFindEmoji = true; auto canFindEmoji = true;
for (auto ch = emojiEnd; ch != end;) { for (auto ch = emojiEnd; ch != end;) {
auto emojiLength = 0; auto emojiLength = 0;
@ -234,9 +235,9 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities)
(newEmojiEnd == end || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) && (newEmojiEnd == end || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
(currentEntity == entitiesEnd || (ch < emojiStart + currentEntity->offset() && newEmojiEnd <= emojiStart + currentEntity->offset()) || (ch >= emojiStart + currentEntity->offset() + currentEntity->length() && newEmojiEnd > emojiStart + currentEntity->offset() + currentEntity->length())) (currentEntity == entitiesEnd || (ch < emojiStart + currentEntity->offset() && newEmojiEnd <= emojiStart + currentEntity->offset()) || (ch >= emojiStart + currentEntity->offset() + currentEntity->length() && newEmojiEnd > emojiStart + currentEntity->offset() + currentEntity->length()))
) { ) {
if (result.isEmpty()) result.reserve(text.size()); if (newText.text.isEmpty()) newText.text.reserve(result.text.size());
appendPartToResult(result, emojiStart, emojiEnd, ch, inOutEntities); AppendPartToResult(newText, emojiStart, emojiEnd, ch);
if (emoji->hasVariants()) { if (emoji->hasVariants()) {
auto it = cEmojiVariants().constFind(emoji->nonColoredId()); auto it = cEmojiVariants().constFind(emoji->nonColoredId());
@ -244,7 +245,7 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities)
emoji = emoji->variant(it.value()); emoji = emoji->variant(it.value());
} }
} }
result.append(emoji->text()); newText.text.append(emoji->text());
ch = emojiEnd = newEmojiEnd; ch = emojiEnd = newEmojiEnd;
canFindEmoji = true; canFindEmoji = true;
@ -257,11 +258,12 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities)
++ch; ++ch;
} }
} }
if (result.isEmpty()) return text; if (newText.text.isEmpty()) {
result.entities = std::move(newText.entities);
appendPartToResult(result, emojiStart, emojiEnd, end, inOutEntities); } else {
AppendPartToResult(newText, emojiStart, emojiEnd, end);
return result; result = std::move(newText);
}
} }
inline RecentEmojiPack &GetRecent() { inline RecentEmojiPack &GetRecent() {

View File

@ -493,33 +493,32 @@ public:
} }
TextParser(Text *t, const QString &text, const TextParseOptions &options) : _t(t), TextParser(Text *t, const QString &text, const TextParseOptions &options) : _t(t),
src(text), source { text },
rich(options.flags & TextParseRichText), rich(options.flags & TextParseRichText),
multiline(options.flags & TextParseMultiline), multiline(options.flags & TextParseMultiline),
stopAfterWidth(QFIXED_MAX) { stopAfterWidth(QFIXED_MAX) {
if (options.flags & TextParseLinks) { if (options.flags & TextParseLinks) {
textParseEntities(src, options.flags, &entities, rich); TextUtilities::ParseEntities(source, options.flags, rich);
} }
parse(options); parse(options);
} }
TextParser(Text *t, const TextWithEntities &textWithEntities, const TextParseOptions &options) : _t(t), TextParser(Text *t, const TextWithEntities &textWithEntities, const TextParseOptions &options) : _t(t),
src(textWithEntities.text), source(textWithEntities),
rich(options.flags & TextParseRichText), rich(options.flags & TextParseRichText),
multiline(options.flags & TextParseMultiline), multiline(options.flags & TextParseMultiline),
stopAfterWidth(QFIXED_MAX) { stopAfterWidth(QFIXED_MAX) {
auto preparsed = textWithEntities.entities; auto &preparsed = textWithEntities.entities;
if ((options.flags & TextParseLinks) && !preparsed.isEmpty()) { if ((options.flags & TextParseLinks) && !preparsed.isEmpty()) {
bool parseMentions = (options.flags & TextParseMentions); bool parseMentions = (options.flags & TextParseMentions);
bool parseHashtags = (options.flags & TextParseHashtags); bool parseHashtags = (options.flags & TextParseHashtags);
bool parseBotCommands = (options.flags & TextParseBotCommands); bool parseBotCommands = (options.flags & TextParseBotCommands);
bool parseMono = (options.flags & TextParseMono); bool parseMono = (options.flags & TextParseMono);
if (parseMentions && parseHashtags && parseBotCommands && parseMono) { if (!parseMentions || !parseHashtags || !parseBotCommands || !parseMono) {
entities = preparsed;
} else {
int32 i = 0, l = preparsed.size(); int32 i = 0, l = preparsed.size();
entities.reserve(l); source.entities.clear();
const QChar s = src.size(); source.entities.reserve(l);
const QChar s = source.text.size();
for (; i < l; ++i) { for (; i < l; ++i) {
auto type = preparsed.at(i).type(); auto type = preparsed.at(i).type();
if (((type == EntityInTextMention || type == EntityInTextMentionName) && !parseMentions) || if (((type == EntityInTextMention || type == EntityInTextMentionName) && !parseMentions) ||
@ -528,7 +527,7 @@ public:
((type == EntityInTextBold || type == EntityInTextItalic || type == EntityInTextCode || type == EntityInTextPre) && !parseMono)) { ((type == EntityInTextBold || type == EntityInTextItalic || type == EntityInTextCode || type == EntityInTextPre) && !parseMono)) {
continue; continue;
} }
entities.push_back(preparsed.at(i)); source.entities.push_back(preparsed.at(i));
} }
} }
} }
@ -540,13 +539,13 @@ public:
stopAfterWidth = ((options.maxh / _t->_st->font->height) + 1) * options.maxw; stopAfterWidth = ((options.maxh / _t->_st->font->height) + 1) * options.maxw;
} }
start = src.constData(); start = source.text.constData();
end = start + src.size(); end = start + source.text.size();
entitiesEnd = entities.cend(); entitiesEnd = source.entities.cend();
waitingEntity = entities.cbegin(); waitingEntity = source.entities.cbegin();
while (waitingEntity != entitiesEnd && waitingEntity->length() <= 0) ++waitingEntity; while (waitingEntity != entitiesEnd && waitingEntity->length() <= 0) ++waitingEntity;
int firstMonospaceOffset = EntityInText::firstMonospaceOffset(entities, end - start); int firstMonospaceOffset = EntityInText::firstMonospaceOffset(source.entities, end - start);
ptr = start; ptr = start;
while (ptr != end && chIsTrimmed(*ptr, rich) && ptr != start + firstMonospaceOffset) { while (ptr != end && chIsTrimmed(*ptr, rich) && ptr != start + firstMonospaceOffset) {
@ -587,50 +586,50 @@ public:
removeFlags.clear(); removeFlags.clear();
_t->_links.resize(maxLnkIndex); _t->_links.resize(maxLnkIndex);
for (Text::TextBlocks::const_iterator i = _t->_blocks.cbegin(), e = _t->_blocks.cend(); i != e; ++i) { for (auto i = _t->_blocks.cbegin(), e = _t->_blocks.cend(); i != e; ++i) {
ITextBlock *b = *i; auto b = *i;
if (b->lnkIndex() > 0x8000) { if (b->lnkIndex() > 0x8000) {
lnkIndex = maxLnkIndex + (b->lnkIndex() - 0x8000); lnkIndex = maxLnkIndex + (b->lnkIndex() - 0x8000);
if (_t->_links.size() < lnkIndex) { if (_t->_links.size() < lnkIndex) {
_t->_links.resize(lnkIndex); _t->_links.resize(lnkIndex);
const TextLinkData &link(links[lnkIndex - maxLnkIndex - 1]); auto &link = links[lnkIndex - maxLnkIndex - 1];
ClickHandlerPtr handler; ClickHandlerPtr handler;
switch (link.type) { switch (link.type) {
case EntityInTextCustomUrl: handler.reset(new HiddenUrlClickHandler(link.data)); break; case EntityInTextCustomUrl: handler = MakeShared<HiddenUrlClickHandler>(link.data); break;
case EntityInTextEmail: case EntityInTextEmail:
case EntityInTextUrl: handler.reset(new UrlClickHandler(link.data, link.displayStatus == LinkDisplayedFull)); break; case EntityInTextUrl: handler = MakeShared<UrlClickHandler>(link.data, link.displayStatus == LinkDisplayedFull); break;
case EntityInTextBotCommand: handler.reset(new BotCommandClickHandler(link.data)); break; case EntityInTextBotCommand: handler = MakeShared<BotCommandClickHandler>(link.data); break;
case EntityInTextHashtag: case EntityInTextHashtag:
if (options.flags & TextTwitterMentions) { if (options.flags & TextTwitterMentions) {
handler.reset(new UrlClickHandler(qsl("https://twitter.com/hashtag/") + link.data.mid(1) + qsl("?src=hash"), true)); handler = MakeShared<UrlClickHandler>(qsl("https://twitter.com/hashtag/") + link.data.mid(1) + qsl("?src=hash"), true);
} else if (options.flags & TextInstagramMentions) { } else if (options.flags & TextInstagramMentions) {
handler.reset(new UrlClickHandler(qsl("https://instagram.com/explore/tags/") + link.data.mid(1) + '/', true)); handler = MakeShared<UrlClickHandler>(qsl("https://instagram.com/explore/tags/") + link.data.mid(1) + '/', true);
} else { } else {
handler.reset(new HashtagClickHandler(link.data)); handler = MakeShared<HashtagClickHandler>(link.data);
} }
break; break;
case EntityInTextMention: case EntityInTextMention:
if (options.flags & TextTwitterMentions) { if (options.flags & TextTwitterMentions) {
handler.reset(new UrlClickHandler(qsl("https://twitter.com/") + link.data.mid(1), true)); handler = MakeShared<UrlClickHandler>(qsl("https://twitter.com/") + link.data.mid(1), true);
} else if (options.flags & TextInstagramMentions) { } else if (options.flags & TextInstagramMentions) {
handler.reset(new UrlClickHandler(qsl("https://instagram.com/") + link.data.mid(1) + '/', true)); handler = MakeShared<UrlClickHandler>(qsl("https://instagram.com/") + link.data.mid(1) + '/', true);
} else { } else {
handler.reset(new MentionClickHandler(link.data)); handler = MakeShared<MentionClickHandler>(link.data);
} }
break; break;
case EntityInTextMentionName: { case EntityInTextMentionName: {
UserId userId = 0; auto fields = TextUtilities::MentionNameDataToFields(link.data);
uint64 accessHash = 0; if (fields.userId) {
if (mentionNameToFields(link.data, &userId, &accessHash)) { handler = MakeShared<MentionNameClickHandler>(link.text, fields.userId, fields.accessHash);
handler.reset(new MentionNameClickHandler(link.text, userId, accessHash));
} else { } else {
LOG(("Bad mention name: %1").arg(link.data)); LOG(("Bad mention name: %1").arg(link.data));
} }
} break; } break;
} }
t_assert(!handler.isNull()); if (!handler.isNull()) {
_t->setLink(lnkIndex, handler); _t->setLink(lnkIndex, handler);
}
} }
b->setLnkIndex(lnkIndex); b->setLnkIndex(lnkIndex);
} }
@ -667,11 +666,9 @@ private:
} }
Text *_t; Text *_t;
QString src; TextWithEntities source;
const QChar *start, *end, *ptr; const QChar *start, *end, *ptr;
bool rich, multiline; bool rich, multiline;
EntitiesInText entities;
EntitiesInText::const_iterator waitingEntity, entitiesEnd; EntitiesInText::const_iterator waitingEntity, entitiesEnd;
typedef QVector<TextLinkData> TextLinks; typedef QVector<TextLinkData> TextLinks;

View File

@ -258,15 +258,6 @@ inline TextSelection unshiftSelection(TextSelection selection, const Text &byTex
return unshiftSelection(selection, byText.length()); return unshiftSelection(selection, byText.length());
} }
void initLinkSets();
const QSet<int32> &validProtocols();
const QSet<int32> &validTopDomains();
const QRegularExpression &reDomain();
const QRegularExpression &reMailName();
const QRegularExpression &reMailStart();
const QRegularExpression &reHashtag();
const QRegularExpression &reBotCommand();
// textcmd // textcmd
QString textcmdSkipBlock(ushort w, ushort h); QString textcmdSkipBlock(ushort w, ushort h);
QString textcmdStartLink(ushort lnkIndex); QString textcmdStartLink(ushort lnkIndex);

File diff suppressed because it is too large Load Diff

View File

@ -116,22 +116,6 @@ struct TextWithEntities {
QString text; QString text;
EntitiesInText entities; 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);
QString textRichPrepare(const QString &text);
QString textOneLine(const QString &text, bool trim = true, bool rich = false);
QString textAccentFold(const QString &text);
QString textSearchKey(const QString &text);
bool textSplit(QString &sendingText, EntitiesInText &sendingEntities, QString &leftText, EntitiesInText &leftEntities, int32 limit);
enum { enum {
TextParseMultiline = 0x001, TextParseMultiline = 0x001,
@ -148,39 +132,91 @@ enum {
TextInstagramHashtags = 0x800, TextInstagramHashtags = 0x800,
}; };
inline bool mentionNameToFields(const QString &data, int32 *outUserId, uint64 *outAccessHash) { // Parsing helpers.
namespace TextUtilities {
bool IsValidProtocol(const QString &protocol);
bool IsValidTopDomain(const QString &domain);
const QRegularExpression &RegExpDomain();
const QRegularExpression &RegExpDomainExplicit();
const QRegularExpression &RegExpMailNameAtEnd();
const QRegularExpression &RegExpHashtag();
const QRegularExpression &RegExpMention();
const QRegularExpression &RegExpBotCommand();
const QRegularExpression &RegExpMonoInline();
const QRegularExpression &RegExpMonoBlock();
inline void Append(TextWithEntities &to, TextWithEntities &&append) {
auto entitiesShiftRight = to.text.size();
for (auto &entity : append.entities) {
entity.shiftRight(entitiesShiftRight);
}
to.text += append.text;
to.entities += append.entities;
}
// Text preprocess.
QString Clean(const QString &text);
QString EscapeForRichParsing(const QString &text);
QString SingleLine(const QString &text);
QString RemoveAccents(const QString &text);
QStringList PrepareSearchWords(const QString &query, const QRegularExpression *SplitterOverride = nullptr);
bool CutPart(TextWithEntities &sending, TextWithEntities &left, int limit);
struct MentionNameFields {
MentionNameFields(int32 userId = 0, uint64 accessHash = 0) : userId(userId), accessHash(accessHash) {
}
int32 userId = 0;
uint64 accessHash = 0;
};
inline MentionNameFields MentionNameDataToFields(const QString &data) {
auto components = data.split('.'); auto components = data.split('.');
if (!components.isEmpty()) { if (!components.isEmpty()) {
*outUserId = components.at(0).toInt(); return { components.at(0).toInt(), (components.size() > 1) ? components.at(1).toULongLong() : 0 };
*outAccessHash = (components.size() > 1) ? components.at(1).toULongLong() : 0;
return (*outUserId != 0);
} }
return false; return MentionNameFields {};
} }
inline QString mentionNameFromFields(int32 userId, uint64 accessHash) { inline QString MentionNameDataFromFields(const MentionNameFields &fields) {
return QString::number(userId) + '.' + QString::number(accessHash); auto result = QString::number(fields.userId);
if (fields.accessHash) {
result += '.' + QString::number(fields.accessHash);
}
return result;
} }
EntitiesInText entitiesFromMTP(const QVector<MTPMessageEntity> &entities); EntitiesInText EntitiesFromMTP(const QVector<MTPMessageEntity> &entities);
MTPVector<MTPMessageEntity> linksToMTP(const EntitiesInText &links, bool sending = false); enum class ConvertOption {
WithLocal,
SkipLocal,
};
MTPVector<MTPMessageEntity> EntitiesToMTP(const EntitiesInText &links, ConvertOption option = ConvertOption::WithLocal);
// New entities are added to the ones that are already in inOutEntities. // New entities are added to the ones that are already in result.
// Changes text if (flags & TextParseMono). // Changes text if (flags & TextParseMono).
void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities, bool rich = false); void ParseEntities(TextWithEntities &result, int32 flags, bool rich = false);
QString textApplyEntities(const QString &text, const EntitiesInText &entities); QString ApplyEntities(const TextWithEntities &text);
QString prepareTextWithEntities(QString result, int32 flags, EntitiesInText *inOutEntities); void PrepareForSending(TextWithEntities &result, int32 flags);
void Trim(TextWithEntities &result);
inline QString prepareText(QString result, bool checkLinks = false) { enum class PrepareTextOption {
EntitiesInText entities; IgnoreLinks,
auto prepareFlags = checkLinks ? (TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands) : 0; CheckLinks,
return prepareTextWithEntities(result, prepareFlags, &entities); };
inline QString PrepareForSending(const QString &text, PrepareTextOption option = PrepareTextOption::IgnoreLinks) {
auto result = TextWithEntities { text };
auto prepareFlags = (option == PrepareTextOption::CheckLinks) ? (TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands) : 0;
PrepareForSending(result, prepareFlags);
return result.text;
} }
// replace bad symbols with space and remove \r // Replace bad symbols with space and remove '\r'.
void cleanTextWithEntities(QString &result, EntitiesInText *inOutEntities); void ApplyServerCleaning(TextWithEntities &result);
void trimTextWithEntities(QString &result, EntitiesInText *inOutEntities);
} // namespace TextUtilities
namespace Lang { namespace Lang {
@ -203,4 +239,4 @@ struct ReplaceTag<TextWithEntities> {
}; };
} } // namespace Lang

View File

@ -39,7 +39,7 @@ Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent)
if (_multiline) { if (_multiline) {
toastOptions.maxh *= kToastMaxLines; toastOptions.maxh *= kToastMaxLines;
} }
_text.setText(st::toastTextStyle, _multiline ? config.text : textOneLine(config.text), toastOptions); _text.setText(st::toastTextStyle, _multiline ? config.text : TextUtilities::SingleLine(config.text), toastOptions);
setAttribute(Qt::WA_TransparentForMouseEvents); setAttribute(Qt::WA_TransparentForMouseEvents);

View File

@ -840,25 +840,22 @@ void FlatTextarea::parseLinks() { // some code is duplicated in text.cpp!
return; return;
} }
initLinkSets(); auto len = text.size();
int32 len = text.size();
const QChar *start = text.unicode(), *end = start + text.size(); const QChar *start = text.unicode(), *end = start + text.size();
for (int32 offset = 0, matchOffset = offset; offset < len;) { for (auto offset = 0, matchOffset = offset; offset < len;) {
QRegularExpressionMatch m = reDomain().match(text, matchOffset); auto m = TextUtilities::RegExpDomain().match(text, matchOffset);
if (!m.hasMatch()) break; if (!m.hasMatch()) break;
int32 domainOffset = m.capturedStart(); auto domainOffset = m.capturedStart();
QString protocol = m.captured(1).toLower(); auto protocol = m.captured(1).toLower();
QString topDomain = m.captured(3).toLower(); auto topDomain = m.captured(3).toLower();
auto isProtocolValid = protocol.isEmpty() || TextUtilities::IsValidProtocol(protocol);
bool isProtocolValid = protocol.isEmpty() || validProtocols().contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); auto isTopDomainValid = !protocol.isEmpty() || TextUtilities::IsValidTopDomain(topDomain);
bool isTopDomainValid = !protocol.isEmpty() || validTopDomains().contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) { if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
QString forMailName = text.mid(offset, domainOffset - offset - 1); auto forMailName = text.mid(offset, domainOffset - offset - 1);
QRegularExpressionMatch mMailName = reMailName().match(forMailName); auto mMailName = TextUtilities::RegExpMailNameAtEnd().match(forMailName);
if (mMailName.hasMatch()) { if (mMailName.hasMatch()) {
offset = matchOffset = m.capturedEnd(); offset = matchOffset = m.capturedEnd();
continue; continue;

View File

@ -164,7 +164,7 @@ void EditorBlock::Row::fillValueString() {
void EditorBlock::Row::fillSearchIndex() { void EditorBlock::Row::fillSearchIndex() {
_searchWords.clear(); _searchWords.clear();
_searchStartChars.clear(); _searchStartChars.clear();
auto toIndex = _name + ' ' + _copyOf + ' ' + textAccentFold(_description.originalText()) + ' ' + _valueString; auto toIndex = _name + ' ' + _copyOf + ' ' + TextUtilities::RemoveAccents(_description.originalText()) + ' ' + _valueString;
auto words = toIndex.toLower().split(SearchSplitter, QString::SkipEmptyParts); auto words = toIndex.toLower().split(SearchSplitter, QString::SkipEmptyParts);
for_const (auto &word, words) { for_const (auto &word, words) {
_searchWords.insert(word); _searchWords.insert(word);
@ -346,11 +346,8 @@ void EditorBlock::scrollToSelected() {
} }
void EditorBlock::searchByQuery(QString query) { void EditorBlock::searchByQuery(QString query) {
auto searchWords = QStringList(); auto words = TextUtilities::PrepareSearchWords(query, &SearchSplitter);
if (!query.isEmpty()) { query = words.isEmpty() ? QString() : words.join(' ');
searchWords = textAccentFold(query.trimmed().toLower()).split(SearchSplitter, QString::SkipEmptyParts);
query = searchWords.join(' ');
}
if (_searchQuery != query) { if (_searchQuery != query) {
setSelected(-1); setSelected(-1);
setPressed(-1); setPressed(-1);
@ -359,7 +356,7 @@ void EditorBlock::searchByQuery(QString query) {
_searchResults.clear(); _searchResults.clear();
auto toFilter = OrderedSet<int>(); auto toFilter = OrderedSet<int>();
for_const (auto &word, searchWords) { for_const (auto &word, words) {
if (word.isEmpty()) continue; if (word.isEmpty()) continue;
auto testToFilter = _searchIndex.value(word[0]); auto testToFilter = _searchIndex.value(word[0]);
@ -371,8 +368,8 @@ void EditorBlock::searchByQuery(QString query) {
} }
} }
if (!toFilter.isEmpty()) { if (!toFilter.isEmpty()) {
auto allWordsFound = [&searchWords](const Row &row) { auto allWordsFound = [&words](const Row &row) {
for_const (auto &word, searchWords) { for_const (auto &word, words) {
if (!row.searchWordsContain(word)) { if (!row.searchWordsContain(word)) {
return false; return false;
} }