EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) { EntitiesInText result; if (tags.isEmpty()) { return result; } result.reserve(tags.size()); for (const auto &tag : tags) { const auto push = [&]( EntityInTextType type, const QString &data = QString()) { result.push_back( EntityInText(type, tag.offset, tag.length, data)); }; if (IsMentionLink(tag.id)) { if (auto match = qthelp::regex_match("^(\\d+\\.\\d+)(/|$)", tag.id.midRef(kMentionTagStart.size()))) { push(EntityInTextMentionName, match->captured(1)); } } else if (tag.id == Ui::InputField::kTagBold) { push(EntityInTextBold); } else if (tag.id == Ui::InputField::kTagItalic) { push(EntityInTextItalic); } else if (tag.id == Ui::InputField::kTagCode) { push(EntityInTextCode); } else if (tag.id == Ui::InputField::kTagPre) { push(EntityInTextPre); } else /*if (ValidateUrl(tag.id)) */{ // We validate when we insert. push(EntityInTextCustomUrl, tag.id); } } return result; }
TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) { TextWithTags::Tags result; if (entities.isEmpty()) { return result; } result.reserve(entities.size()); for (const auto &entity : entities) { const auto push = [&](const QString &tag) { result.push_back({ entity.offset(), entity.length(), tag }); }; switch (entity.type()) { case EntityInTextMentionName: { auto match = QRegularExpression("^(\\d+\\.\\d+)$").match(entity.data()); if (match.hasMatch()) { push(kMentionTagStart + entity.data()); } } break; case EntityInTextCustomUrl: { const auto url = entity.data(); if (Ui::InputField::IsValidMarkdownLink(url) && !IsMentionLink(url)) { push(url); } } break; case EntityInTextBold: push(Ui::InputField::kTagBold); break; case EntityInTextItalic: push(Ui::InputField::kTagItalic); break; case EntityInTextCode: push(Ui::InputField::kTagCode); break; case EntityInTextPre: push(Ui::InputField::kTagPre); break; } } return result; }
TextWithTags::Tags ConvertEntitiesToTextTags(const EntitiesInText &entities) { TextWithTags::Tags result; if (entities.isEmpty()) { return result; } result.reserve(entities.size()); for (const auto &entity : entities) { const auto push = [&](const QString &tag) { result.push_back({ entity.offset(), entity.length(), tag }); }; switch (entity.type()) { case EntityType::MentionName: { auto match = QRegularExpression(R"(^(\d+\.\d+)$)").match(entity.data()); if (match.hasMatch()) { push(kMentionTagStart + entity.data()); } } break; case EntityType::CustomUrl: { const auto url = entity.data(); if (Ui::InputField::IsValidMarkdownLink(url) && !IsMentionLink(url)) { push(url); } } break; case EntityType::Bold: push(Ui::InputField::kTagBold); break; case EntityType::Italic: push(Ui::InputField::kTagItalic); break; // #TODO entities case EntityType::Code: push(Ui::InputField::kTagCode); break; case EntityType::Pre: push(Ui::InputField::kTagPre); break; } } return result; } std::unique_ptr<QMimeData> MimeDataFromText( const TextForMimeData &text) { if (text.empty()) { return nullptr; } auto result = std::make_unique<QMimeData>(); result->setText(text.expanded); auto tags = ConvertEntitiesToTextTags(text.rich.entities); if (!tags.isEmpty()) { for (auto &tag : tags) { tag.id = ConvertTagToMimeTag(tag.id); } result->setData( TextUtilities::TagsTextMimeType(), text.rich.text.toUtf8()); result->setData( TextUtilities::TagsMimeType(), TextUtilities::SerializeTags(tags)); } return result; } void SetClipboardText( const TextForMimeData &text, QClipboard::Mode mode) { if (auto data = MimeDataFromText(text)) { QApplication::clipboard()->setMimeData(data.release(), mode); } } Fn<bool( Ui::InputField::EditLinkSelection selection, QString text, QString link, EditLinkAction action)> DefaultEditLinkCallback( not_null<Ui::InputField*> field) { const auto weak = make_weak(field); return [=]( EditLinkSelection selection, QString text, QString link, EditLinkAction action) { if (action == EditLinkAction::Check) { return Ui::InputField::IsValidMarkdownLink(link) && !IsMentionLink(link); } Ui::show(Box<EditLinkBox>(text, link, [=]( const QString &text, const QString &link) { if (const auto strong = weak.data()) { strong->commitMarkdownLinkEdit(selection, text, link); } }), LayerOption::KeepOther); return true; }; } void InitMessageField( not_null<Window::Controller*> controller, not_null<Ui::InputField*> field) { field->setMinHeight(st::historySendSize.height() - 2 * st::historySendPadding); field->setMaxHeight(st::historyComposeFieldMaxHeight); field->setTagMimeProcessor(std::make_unique<FieldTagMimeProcessor>()); field->document()->setDocumentMargin(4.); field->setAdditionalMargin(ConvertScale(4) - 4); field->customTab(true); field->setInstantReplaces(Ui::InstantReplaces::Default()); field->setInstantReplacesEnabled(Global::ReplaceEmojiValue()); field->setMarkdownReplacesEnabled(rpl::single(true)); field->setEditLinkCallback(DefaultEditLinkCallback(field)); } bool HasSendText(not_null<const Ui::InputField*> field) { const auto &text = field->getTextWithTags().text; for (const auto ch : text) { const auto code = ch.unicode(); if (code != ' ' && code != '\n' && code != '\r' && !chReplacedBySpace(code)) { return true; } } return false; } InlineBotQuery ParseInlineBotQuery(not_null<const Ui::InputField*> field) { auto result = InlineBotQuery(); const auto &full = field->getTextWithTags(); const auto &text = full.text; const auto textLength = text.size(); auto inlineUsernameStart = 1; auto inlineUsernameLength = 0; if (textLength > 2 && text[0] == '@' && text[1].isLetter()) { inlineUsernameLength = 1; for (auto i = inlineUsernameStart + 1; i != textLength; ++i) { const auto ch = text[i]; if (ch.isLetterOrNumber() || ch.unicode() == '_') { ++inlineUsernameLength; continue; } else if (!ch.isSpace()) { inlineUsernameLength = 0; } break; } auto inlineUsernameEnd = inlineUsernameStart + inlineUsernameLength; auto inlineUsernameEqualsText = (inlineUsernameEnd == textLength); auto validInlineUsername = false; if (inlineUsernameEqualsText) { validInlineUsername = text.endsWith(qstr("bot")); } else if (inlineUsernameEnd < textLength && inlineUsernameLength) { validInlineUsername = text[inlineUsernameEnd].isSpace(); } if (validInlineUsername) { if (!full.tags.isEmpty() && (full.tags.front().offset < inlineUsernameStart + inlineUsernameLength)) { return InlineBotQuery(); } auto username = text.midRef(inlineUsernameStart, inlineUsernameLength); if (username != result.username) { result.username = username.toString(); if (const auto peer = Auth().data().peerByUsername(result.username)) { if (const auto user = peer->asUser()) { result.bot = peer->asUser(); } else { result.bot = nullptr; } result.lookingUpBot = false; } else { result.bot = nullptr; result.lookingUpBot = true; } } if (result.lookingUpBot) { result.query = QString(); return result; } else if (result.bot && (!result.bot->botInfo || result.bot->botInfo->inlinePlaceholder.isEmpty())) { result.bot = nullptr; } else { result.query = inlineUsernameEqualsText ? QString() : text.mid(inlineUsernameEnd + 1); return result; } } else { inlineUsernameLength = 0; } } if (inlineUsernameLength < 3) { result.bot = nullptr; result.username = QString(); } result.query = QString(); return result; }