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 addLocalChangelog = [this, &addedSome](const QString &text) {
auto textWithEntities = TextWithEntities { text };
textParseEntities(textWithEntities.text, TextParseLinks, &textWithEntities.entities);
TextUtilities::ParseEntities(textWithEntities, TextParseLinks);
App::wnd()->serviceNotification(textWithEntities, MTP_messageMediaEmpty(), unixtime());
addedSome = true;
};
@ -1177,7 +1177,7 @@ void ApiWrap::saveDraftsToCloud() {
if (!textWithTags.tags.isEmpty()) {
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) {
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
// local values for first name and last name already, otherwise skip
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 lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName;
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() ? TextUtilities::SingleLine(qs(d.vlast_name)) : QString()) : data->lastName;
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);
if (phoneChanged) {
@ -714,7 +714,7 @@ namespace {
}
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->setIsForbidden(false);
@ -1096,7 +1096,7 @@ namespace {
}
if (auto existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
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->updateMedia(m.has_media() ? (&m.vmedia) : 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 showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone));
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);
}
@ -1497,13 +1497,13 @@ namespace {
}
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 parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
if (siteName == qstr("Twitter") || siteName == qstr("Instagram")) {
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);
}
@ -1800,15 +1800,15 @@ namespace {
}
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) {
convert->type = toWebPageType(type);
convert->url = textClean(url);
convert->displayUrl = textClean(displayUrl);
convert->siteName = textClean(siteName);
convert->title = textOneLine(textClean(title));
convert->url = TextUtilities::Clean(url);
convert->displayUrl = TextUtilities::Clean(displayUrl);
convert->siteName = TextUtilities::Clean(siteName);
convert->title = TextUtilities::SingleLine(title);
convert->description = description;
convert->photo = photo;
convert->document = document;
convert->duration = duration;
convert->author = textClean(author);
convert->author = TextUtilities::Clean(author);
if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert);
convert->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(convert);
@ -1831,15 +1831,15 @@ namespace {
if (result != convert) {
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) {
result->type = toWebPageType(type);
result->url = textClean(url);
result->displayUrl = textClean(displayUrl);
result->siteName = textClean(siteName);
result->title = textOneLine(textClean(title));
result->url = TextUtilities::Clean(url);
result->displayUrl = TextUtilities::Clean(displayUrl);
result->siteName = TextUtilities::Clean(siteName);
result->title = TextUtilities::SingleLine(title);
result->description = description;
result->photo = photo;
result->document = document;
result->duration = duration;
result->author = textClean(author);
result->author = TextUtilities::Clean(author);
if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result);
result->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(result);
@ -1869,9 +1869,9 @@ namespace {
}
if (!convert->accessHash && accessHash) {
convert->accessHash = accessHash;
convert->shortName = textClean(shortName);
convert->title = textOneLine(textClean(title));
convert->description = textClean(description);
convert->shortName = TextUtilities::Clean(shortName);
convert->title = TextUtilities::SingleLine(title);
convert->description = TextUtilities::Clean(description);
convert->photo = photo;
convert->document = document;
if (App::main()) App::main()->gameUpdated(convert);
@ -1891,9 +1891,9 @@ namespace {
if (result != convert) {
if (!result->accessHash && accessHash) {
result->accessHash = accessHash;
result->shortName = textClean(shortName);
result->title = textOneLine(textClean(title));
result->description = textClean(description);
result->shortName = TextUtilities::Clean(shortName);
result->title = TextUtilities::SingleLine(title);
result->description = TextUtilities::Clean(description);
result->photo = photo;
result->document = document;
if (App::main()) App::main()->gameUpdated(result);

View file

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

View file

@ -1526,22 +1526,11 @@ void ContactsBox::Inner::updateSelection() {
void ContactsBox::Inner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
auto words = TextUtilities::PrepareSearchWords(_lastQuery);
filter = words.isEmpty() ? QString() : words.join(' ');
_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) {
_filter = filter;
@ -1560,10 +1549,10 @@ void ContactsBox::Inner::updateFilter(QString filter) {
} else {
if (!_addContactLnk->isHidden()) _addContactLnk->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();
if (!f.isEmpty()) {
if (!words.isEmpty()) {
const Dialogs::List *toFilter = nullptr;
if (!_contacts->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {

View file

@ -984,7 +984,7 @@ void PeerListBox::Inner::checkScrollForPreload() {
}
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(' ');
if (_normalizedSearchQuery != normalizedQuery) {
setSearchQuery(query, normalizedQuery);

View file

@ -438,7 +438,7 @@ void SendFilesBox::onSend(bool ctrlShiftEnter) {
_confirmed = true;
if (_confirmedCallback) {
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);
}
closeBox();
@ -766,7 +766,7 @@ void EditCaptionBox::onSave(bool ctrlShiftEnter) {
if (!sentEntities.v.isEmpty()) {
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));
}

View file

@ -674,21 +674,9 @@ bool ShareBox::Inner::hasSelected() const {
void ShareBox::Inner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
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(' ');
}
auto words = TextUtilities::PrepareSearchWords(_lastQuery);
filter = words.isEmpty() ? QString() : words.join(' ');
if (_filter != filter) {
_filter = filter;
@ -701,10 +689,10 @@ void ShareBox::Inner::updateFilter(QString filter) {
if (_filter.isEmpty()) {
refresh();
} else {
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
_filtered.clear();
if (!f.isEmpty()) {
if (!words.isEmpty()) {
const Dialogs::List *toFilter = nullptr;
if (!_chatsIndexed->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {

View file

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

View file

@ -175,7 +175,7 @@ bool UsernameBox::onUpdateFail(const RPCError &error) {
_saveRequestId = 0;
QString err(error.type());
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();
return true;
} 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);
if (resetScroll) {
_type = type;
_filter = textAccentFold(plainQuery.toString());
_filter = TextUtilities::RemoveAccents(plainQuery.toString());
}
_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
};
inline const QRegularExpression &cWordSplit() {
static QRegularExpression regexp(qsl("[\\@\\s\\-\\+\\(\\)\\[\\]\\{\\}\\<\\>\\,\\.\\:\\!\\_\\;\\\"\\'\\x0]"));
return regexp;
}
inline const QRegularExpression &cRussianLetters() {
static QRegularExpression regexp(QString::fromUtf8("[а-яА-ЯёЁ]"));
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) {
auto history = App::history(peerId);
auto text = qs(draft.vmessage);
auto entities = draft.has_entities() ? entitiesFromMTP(draft.ventities.v) : EntitiesInText();
TextWithTags textWithTags = { textApplyEntities(text, entities), ConvertEntitiesToTextTags(entities) };
MsgId replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : 0;
auto text = TextWithEntities { qs(draft.vmessage), draft.has_entities() ? TextUtilities::EntitiesFromMTP(draft.ventities.v) : EntitiesInText() };
auto textWithTags = TextWithTags { TextUtilities::ApplyEntities(text), ConvertEntitiesToTextTags(text.entities) };
auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0);
auto cloudDraft = std::make_unique<Draft>(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage());
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->cloudDraftTextCache.isEmpty()) {
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);
}
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) {
newFilter = textSearchKey(newFilter);
auto words = TextUtilities::PrepareSearchWords(newFilter);
newFilter = words.isEmpty() ? QString() : words.join(' ');
if (newFilter != _filter || force) {
QStringList f;
if (!newFilter.isEmpty()) {
QStringList filterList = newFilter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
_filter = newFilter;
if (!_searchInPeer && _filter.isEmpty()) {
_state = DefaultState;
_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);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
newFilter = f.join(' ');
}
if (newFilter != _filter || force) {
_filter = newFilter;
if (!_searchInPeer && _filter.isEmpty()) {
_state = DefaultState;
_hashtagResults.clear();
_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;
}
_state = FilteredState;
_filterResults.clear();
if (!_searchInPeer && !words.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()) {
for (fi = fb; fi != fe; ++fi) {
auto found = _contactsNoDialogs->filtered(fi->at(0));
if (found->isEmpty()) {
toFilterContacts = nullptr;
break;
}
if (!toFilterContacts || toFilterContacts->size() > found->size()) {
toFilterContacts = found;
}
}
const Dialogs::List *toFilterContacts = nullptr;
if (!_contactsNoDialogs->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
auto found = _contactsNoDialogs->filtered(fi->at(0));
if (found->isEmpty()) {
toFilterContacts = nullptr;
break;
}
if (!toFilterContacts || toFilterContacts->size() > found->size()) {
toFilterContacts = found;
}
}
_filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0));
if (toFilter) {
for_const (auto row, *toFilter) {
const PeerData::Names &names(row->history()->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
for (ni = nb; ni != ne; ++ni) {
if (ni->startsWith(*fi)) {
break;
}
}
if (ni == ne) {
}
_filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0));
if (toFilter) {
for_const (auto row, *toFilter) {
const PeerData::Names &names(row->history()->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
for (ni = nb; ni != ne; ++ni) {
if (ni->startsWith(*fi)) {
break;
}
}
if (fi == fe) {
_filterResults.push_back(row);
if (ni == ne) {
break;
}
}
if (fi == fe) {
_filterResults.push_back(row);
}
}
if (toFilterContacts) {
for_const (auto row, *toFilterContacts) {
const PeerData::Names &names(row->history()->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
for (ni = nb; ni != ne; ++ni) {
if (ni->startsWith(*fi)) {
break;
}
}
if (ni == ne) {
}
if (toFilterContacts) {
for_const (auto row, *toFilterContacts) {
const PeerData::Names &names(row->history()->peer->names);
PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
for (fi = fb; fi != fe; ++fi) {
QString filterName(*fi);
for (ni = nb; ni != ne; ++ni) {
if (ni->startsWith(*fi)) {
break;
}
}
if (fi == fe) {
_filterResults.push_back(row);
if (ni == ne) {
break;
}
}
if (fi == fe) {
_filterResults.push_back(row);
}
}
}
}
@ -2038,10 +2024,10 @@ bool DialogsInner::choosePeer() {
}
void DialogsInner::saveRecentHashtags(const QString &text) {
bool found = false;
auto found = false;
QRegularExpressionMatch m;
RecentHashtagPack recent(cRecentSearchHashtags());
for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) {
auto recent = cRecentSearchHashtags();
for (int32 i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) {
i = m.capturedStart();
next = m.capturedEnd();
if (m.hasMatch()) {

View file

@ -56,11 +56,10 @@ constexpr auto kNewBlockEachMessage = 50;
auto GlobalPinnedIndex = 0;
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")));
EntitiesInText entities;
textParseEntities(text, _historyTextNoMonoOptions.flags, &entities);
entities.push_front(EntityInText(EntityInTextItalic, 0, text.size()));
return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, { text, entities });
auto text = TextWithEntities { lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")) };
TextUtilities::ParseEntities(text, _historyTextNoMonoOptions.flags);
text.entities.push_front(EntityInText(EntityInTextItalic, 0, text.text.size()));
return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, text);
}
} // 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) };
text.entities.append(EntityInText(EntityInTextBold, 0, text.text.size()));
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);
text.text.append(qstr("\n\n") + description);
_emptyText.setMarkedText(st::defaultTextStyle, text, options);

View file

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

View file

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

View file

@ -115,7 +115,7 @@ ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s)
auto str = row.at(j).text;
button.type = row.at(j).type;
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();
}
_rows.push_back(newRow);
@ -1141,12 +1141,12 @@ QString HistoryItem::inDialogsText() const {
if (emptyText()) {
return _media ? _media->inDialogsText() : QString();
}
return textClean(_text.originalText());
return TextUtilities::Clean(_text.originalText());
};
auto plainText = getText();
if ((!_history->peer->isUser() || out()) && !isPost() && !isEmpty()) {
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 plainText;

View file

@ -42,7 +42,7 @@ public:
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString inDialogsText() const {
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;

View file

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

View file

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

View file

@ -89,7 +89,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto prepareChatCreate = [this](const MTPDmessageActionChatCreate &action) {
auto result = PreparedText {};
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;
};
@ -99,7 +99,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
result.text = lang(lng_action_created_channel);
} else {
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;
};
@ -143,10 +143,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
auto prepareChatEditTitle = [this](const MTPDmessageActionChatEditTitle &action) {
auto result = PreparedText {};
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 {
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;
};
@ -423,7 +423,7 @@ TextWithEntities HistoryService::selectedText(TextSelection selection) const {
}
QString HistoryService::inDialogsText() const {
return textcmdLink(1, textClean(notificationText()));
return textcmdLink(1, TextUtilities::Clean(notificationText()));
}
QString HistoryService::inReplyText() const {

View file

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

View file

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

View file

@ -136,7 +136,7 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
case mtpc_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());
if (result->_type == Type::Photo) {
result->createPhoto();

View file

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

View file

@ -1479,23 +1479,24 @@ void MainWidget::sendMessage(const MessageToSend &message) {
}
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;
QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities);
TextUtilities::PrepareForSending(left, prepareFlags);
HistoryItem *lastMessage = nullptr;
MsgId replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo;
while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) {
FullMsgId newId(peerToChannel(history->peer->id), clientMsgId());
uint64 randomId = rand_value<uint64>();
auto replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
auto newId = FullMsgId(peerToChannel(history->peer->id), clientMsgId());
auto randomId = rand_value<uint64>();
trimTextWithEntities(sendingText, &sendingEntities);
TextUtilities::Trim(sending);
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 sendFlags = MTPmessages_SendMessage::Flags(0);
if (replyTo) {
@ -1523,8 +1524,8 @@ void MainWidget::sendMessage(const MessageToSend &message) {
if (silentPost) {
sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
}
auto localEntities = linksToMTP(sendingEntities);
auto sentEntities = linksToMTP(sendingEntities, true);
auto localEntities = TextUtilities::EntitiesToMTP(sending.entities);
auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal);
if (!sentEntities.v.isEmpty()) {
sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
}
@ -1546,7 +1547,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
bool found = false;
QRegularExpressionMatch m;
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();
next = m.capturedEnd();
if (m.hasMatch()) {
@ -2093,12 +2094,11 @@ void MainWidget::dialogsCancelled() {
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;
QString sendingText, leftText = message.text;
EntitiesInText sendingEntities, leftEntities = message.entities;
auto sending = TextWithEntities(), left = message;
HistoryItem *item = nullptr;
while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) {
MTPVector<MTPMessageEntity> localEntities = linksToMTP(sendingEntities);
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);
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
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(sending.text), media, MTPnullMarkup, localEntities, MTPint(), MTPint()), NewMessageUnread);
}
if (item) {
_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)) {
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->updateMedia(d.has_media() ? (&d.vmedia) : nullptr);
item->addToOverview(AddToOverviewNew);
@ -5129,9 +5129,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
auto &d = update.c_updateUserName();
if (auto user = App::userLoaded(d.vuser_id.v)) {
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 {
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);
}
@ -5234,7 +5234,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
if (d.is_popup()) {
Ui::show(Box<InformBox>(qs(d.vmessage)));
} 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();
}
} break;

View file

@ -323,7 +323,7 @@ void CoverWidget::handleSongChange() {
if (song->performer.isEmpty()) {
textWithEntities.text = song->title.isEmpty() ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title;
} 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.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() });
}

View file

@ -500,7 +500,7 @@ void Widget::handleSongChange() {
if (!song || song->performer.isEmpty()) {
textWithEntities.text = (!song || song->title.isEmpty()) ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title;
} 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.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);
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 };
_details.setText(st::defaultTextStyle, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts);
_details.setLink(1, goToMessageClickHandler(parent));

View file

@ -309,9 +309,9 @@ void GroupMembersWidget::refreshLimitReached() {
bool limitReachedShown = (itemsCount() >= Global::ChatSizeMax()) && chat->amCreator() && !emptyTitle();
if (limitReachedShown && !_limitReachedInfo) {
_limitReachedInfo.create(this, st::profileLimitReachedLabel);
QString title = textRichPrepare(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax()));
QString body = textRichPrepare(lang(lng_profile_migrate_body));
QString link = textRichPrepare(lang(lng_profile_migrate_learn_more));
QString title = TextUtilities::EscapeForRichParsing(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax()));
QString body = TextUtilities::EscapeForRichParsing(lang(lng_profile_migrate_body));
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);
_limitReachedInfo->setRichText(text);
_limitReachedInfo->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {

View file

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

View file

@ -1120,16 +1120,7 @@ void AddParticipantBoxSearchController::searchGlobalDone(mtpRequestId requestId,
void AddParticipantBoxSearchController::addChatsContacts() {
_chatsContactsAdded = true;
auto filterWordList = _query.split(cWordSplit(), QString::SkipEmptyParts);
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);
}
}
auto wordList = TextUtilities::PrepareSearchWords(_query);
if (wordList.empty()) {
return;
}

View file

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

View file

@ -417,21 +417,8 @@ void CountrySelectBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
}
void CountrySelectBox::Inner::updateFilter(QString filter) {
filter = textSearchKey(filter);
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(' ');
}
auto words = TextUtilities::PrepareSearchWords(filter);
filter = words.isEmpty() ? QString() : words.join(' ');
if (_filter != filter) {
_filter = filter;
@ -441,7 +428,7 @@ void CountrySelectBox::Inner::updateFilter(QString filter) {
QChar first = _filter[0].toLower();
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();
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]);
}
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) {
for (auto &entity : *inOutEntities) {
for (auto &entity : result.entities) {
if (entity.offset() >= to - start) break;
if (entity.offset() + entity.length() < from - start) continue;
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) {
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) {
auto result = QString();
auto currentEntity = inOutEntities->begin();
auto entitiesEnd = inOutEntities->end();
auto emojiStart = text.constData();
inline void ReplaceInText(TextWithEntities &result) {
auto newText = TextWithEntities();
newText.entities = std::move(result.entities);
auto currentEntity = newText.entities.begin();
auto entitiesEnd = newText.entities.end();
auto emojiStart = result.text.constData();
auto emojiEnd = emojiStart;
auto end = emojiStart + text.size();
auto end = emojiStart + result.text.size();
auto canFindEmoji = true;
for (auto ch = emojiEnd; ch != end;) {
auto emojiLength = 0;
@ -234,9 +235,9 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities)
(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()))
) {
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()) {
auto it = cEmojiVariants().constFind(emoji->nonColoredId());
@ -244,7 +245,7 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities)
emoji = emoji->variant(it.value());
}
}
result.append(emoji->text());
newText.text.append(emoji->text());
ch = emojiEnd = newEmojiEnd;
canFindEmoji = true;
@ -257,11 +258,12 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities)
++ch;
}
}
if (result.isEmpty()) return text;
appendPartToResult(result, emojiStart, emojiEnd, end, inOutEntities);
return result;
if (newText.text.isEmpty()) {
result.entities = std::move(newText.entities);
} else {
AppendPartToResult(newText, emojiStart, emojiEnd, end);
result = std::move(newText);
}
}
inline RecentEmojiPack &GetRecent() {

View file

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

View file

@ -258,15 +258,6 @@ inline TextSelection unshiftSelection(TextSelection selection, const Text &byTex
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
QString textcmdSkipBlock(ushort w, ushort h);
QString textcmdStartLink(ushort lnkIndex);

File diff suppressed because it is too large Load diff

View file

@ -116,22 +116,6 @@ struct TextWithEntities {
QString text;
EntitiesInText entities;
};
inline void appendTextWithEntities(TextWithEntities &to, TextWithEntities &&append) {
int entitiesShiftRight = to.text.size();
for (auto &entity : append.entities) {
entity.shiftRight(entitiesShiftRight);
}
to.text += append.text;
to.entities += append.entities;
}
// text preprocess
QString textClean(const QString &text);
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 {
TextParseMultiline = 0x001,
@ -148,39 +132,91 @@ enum {
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('.');
if (!components.isEmpty()) {
*outUserId = components.at(0).toInt();
*outAccessHash = (components.size() > 1) ? components.at(1).toULongLong() : 0;
return (*outUserId != 0);
return { components.at(0).toInt(), (components.size() > 1) ? components.at(1).toULongLong() : 0 };
}
return false;
return MentionNameFields {};
}
inline QString mentionNameFromFields(int32 userId, uint64 accessHash) {
return QString::number(userId) + '.' + QString::number(accessHash);
inline QString MentionNameDataFromFields(const MentionNameFields &fields) {
auto result = QString::number(fields.userId);
if (fields.accessHash) {
result += '.' + QString::number(fields.accessHash);
}
return result;
}
EntitiesInText entitiesFromMTP(const QVector<MTPMessageEntity> &entities);
MTPVector<MTPMessageEntity> linksToMTP(const EntitiesInText &links, bool sending = false);
EntitiesInText EntitiesFromMTP(const QVector<MTPMessageEntity> &entities);
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).
void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities, bool rich = false);
QString textApplyEntities(const QString &text, const EntitiesInText &entities);
void ParseEntities(TextWithEntities &result, int32 flags, bool rich = false);
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) {
EntitiesInText entities;
auto prepareFlags = checkLinks ? (TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands) : 0;
return prepareTextWithEntities(result, prepareFlags, &entities);
enum class PrepareTextOption {
IgnoreLinks,
CheckLinks,
};
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
void cleanTextWithEntities(QString &result, EntitiesInText *inOutEntities);
void trimTextWithEntities(QString &result, EntitiesInText *inOutEntities);
// Replace bad symbols with space and remove '\r'.
void ApplyServerCleaning(TextWithEntities &result);
} // namespace TextUtilities
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) {
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);

View file

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

View file

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