Example #1
0
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;
}
Example #2
0
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;
}