mirror of https://github.com/procxx/kepka.git
Marking tags by random values only inside of FlatTextarea.
Added a strategy to convert tags to and from tags-for-mime-data.
This commit is contained in:
parent
45143c40c9
commit
5a47d8e29b
|
@ -2754,6 +2754,32 @@ EntitiesInText entitiesFromFieldTags(const FlatTextarea::TagList &tags) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// For mention tags save and validate userId, ignore tags for different userId.
|
||||||
|
class FieldTagMimeProcessor : public FlatTextarea::TagMimeProcessor {
|
||||||
|
public:
|
||||||
|
QString mimeTagFromTag(const QString &tagId) override {
|
||||||
|
if (tagId.startsWith(qstr("mention://"))) {
|
||||||
|
return tagId + ':' + QString::number(MTP::authedId());
|
||||||
|
}
|
||||||
|
return tagId;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString tagFromMimeTag(const QString &mimeTag) override {
|
||||||
|
if (mimeTag.startsWith(qstr("mention://"))) {
|
||||||
|
auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
|
||||||
|
if (!match.hasMatch() || match.capturedRef(1).toInt() != MTP::authedId()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
|
||||||
|
}
|
||||||
|
return mimeTag;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||||
, _fieldBarCancel(this, st::replyCancel)
|
, _fieldBarCancel(this, st::replyCancel)
|
||||||
, _scroll(this, st::historyScroll, false)
|
, _scroll(this, st::historyScroll, false)
|
||||||
|
@ -2874,6 +2900,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||||
connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod)));
|
connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod)));
|
||||||
connect(_fieldAutocomplete, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onStickerSend(DocumentData*)));
|
connect(_fieldAutocomplete, SIGNAL(stickerChosen(DocumentData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onStickerSend(DocumentData*)));
|
||||||
_field.installEventFilter(_fieldAutocomplete);
|
_field.installEventFilter(_fieldAutocomplete);
|
||||||
|
_field.setTagMimeProcessor(std_::make_unique<FieldTagMimeProcessor>());
|
||||||
updateFieldSubmitSettings();
|
updateFieldSubmitSettings();
|
||||||
|
|
||||||
_field.hide();
|
_field.hide();
|
||||||
|
@ -2940,7 +2967,7 @@ void HistoryWidget::onMentionInsert(UserData *user) {
|
||||||
} else {
|
} else {
|
||||||
replacement = '@' + user->username;
|
replacement = '@' + user->username;
|
||||||
}
|
}
|
||||||
_field.insertMentionHashtagOrBotCommand(replacement, entityTag);
|
_field.insertTag(replacement, entityTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method) {
|
void HistoryWidget::onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method) {
|
||||||
|
@ -2949,7 +2976,7 @@ void HistoryWidget::onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::
|
||||||
App::sendBotCommand(_peer, nullptr, str);
|
App::sendBotCommand(_peer, nullptr, str);
|
||||||
setFieldText(_field.getLastText().mid(_field.textCursor().position()));
|
setFieldText(_field.getLastText().mid(_field.textCursor().position()));
|
||||||
} else {
|
} else {
|
||||||
_field.insertMentionHashtagOrBotCommand(str);
|
_field.insertTag(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -418,65 +418,75 @@ QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start) const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlatTextarea::insertMentionHashtagOrBotCommand(const QString &data, const QString &tagId) {
|
void FlatTextarea::insertTag(const QString &text, QString tagId) {
|
||||||
QTextCursor c(textCursor());
|
auto cursor = textCursor();
|
||||||
int32 pos = c.position();
|
int32 pos = cursor.position();
|
||||||
|
|
||||||
QTextDocument *doc(document());
|
auto doc = document();
|
||||||
QTextBlock block = doc->findBlock(pos);
|
auto block = doc->findBlock(pos);
|
||||||
for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) {
|
for (auto iter = block.begin(); !iter.atEnd(); ++iter) {
|
||||||
QTextFragment fr(iter.fragment());
|
auto fragment = iter.fragment();
|
||||||
if (!fr.isValid()) continue;
|
t_assert(fragment.isValid());
|
||||||
|
|
||||||
int32 p = fr.position(), e = (p + fr.length());
|
int fragmentPosition = fragment.position();
|
||||||
if (p >= pos || e < pos) continue;
|
int fragmentEnd = (fragmentPosition + fragment.length());
|
||||||
|
if (fragmentPosition >= pos || fragmentEnd < pos) continue;
|
||||||
|
|
||||||
QTextCharFormat f = fr.charFormat();
|
auto format = fragment.charFormat();
|
||||||
if (f.isImageFormat()) continue;
|
if (format.isImageFormat()) continue;
|
||||||
|
|
||||||
bool mentionInCommand = false;
|
bool mentionInCommand = false;
|
||||||
QString t(fr.text());
|
auto fragmentText = fragment.text();
|
||||||
for (int i = pos - p; i > 0; --i) {
|
for (int i = pos - fragmentPosition; i > 0; --i) {
|
||||||
if (t.at(i - 1) == '@' || t.at(i - 1) == '#' || t.at(i - 1) == '/') {
|
auto previousChar = fragmentText.at(i - 1);
|
||||||
if ((i == pos - p || (t.at(i - 1) == '/' ? t.at(i).isLetterOrNumber() : t.at(i).isLetter()) || t.at(i - 1) == '#') && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
|
if (previousChar == '@' || previousChar == '#' || previousChar == '/') {
|
||||||
c.setPosition(p + i - 1, QTextCursor::MoveAnchor);
|
if ((i == pos - fragmentPosition || (previousChar == '/' ? fragmentText.at(i).isLetterOrNumber() : fragmentText.at(i).isLetter()) || previousChar == '#') &&
|
||||||
int till = p + i;
|
(i < 2 || !(fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_'))) {
|
||||||
for (; (till < e) && (till - p - i + 1 < data.size()); ++till) {
|
cursor.setPosition(fragmentPosition + i - 1, QTextCursor::MoveAnchor);
|
||||||
if (t.at(till - p).toLower() != data.at(till - p - i + 1).toLower()) {
|
int till = fragmentPosition + i;
|
||||||
|
for (; (till < fragmentEnd) && (till - fragmentPosition - i + 1 < text.size()); ++till) {
|
||||||
|
if (fragmentText.at(till - fragmentPosition).toLower() != text.at(till - fragmentPosition - i + 1).toLower()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (till - p - i + 1 == data.size() && till < e && t.at(till - p) == ' ') {
|
if (till - fragmentPosition - i + 1 == text.size() && till < fragmentEnd && fragmentText.at(till - fragmentPosition) == ' ') {
|
||||||
++till;
|
++till;
|
||||||
}
|
}
|
||||||
c.setPosition(till, QTextCursor::KeepAnchor);
|
cursor.setPosition(till, QTextCursor::KeepAnchor);
|
||||||
break;
|
break;
|
||||||
} else if ((i == pos - p || t.at(i).isLetter()) && t.at(i - 1) == '@' && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) {
|
} else if ((i == pos - fragmentPosition || fragmentText.at(i).isLetter()) && fragmentText.at(i - 1) == '@' && i > 2 && (fragmentText.at(i - 2).isLetterOrNumber() || fragmentText.at(i - 2) == '_') && !mentionInCommand) {
|
||||||
mentionInCommand = true;
|
mentionInCommand = true;
|
||||||
--i;
|
--i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break;
|
if (pos - fragmentPosition - i > 127 || (!mentionInCommand && (pos - fragmentPosition - i > 63))) break;
|
||||||
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
if (!fragmentText.at(i - 1).isLetterOrNumber() && fragmentText.at(i - 1) != '_') break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (tagId.isEmpty()) {
|
if (tagId.isEmpty()) {
|
||||||
QTextCharFormat format = c.charFormat();
|
QTextCharFormat format = cursor.charFormat();
|
||||||
format.setAnchor(false);
|
format.setAnchor(false);
|
||||||
format.setAnchorName(QString());
|
format.setAnchorName(QString());
|
||||||
format.clearForeground();
|
format.clearForeground();
|
||||||
c.insertText(data + ' ', format);
|
cursor.insertText(text + ' ', format);
|
||||||
} else {
|
} else {
|
||||||
_insertedTags.clear();
|
_insertedTags.clear();
|
||||||
_insertedTags.push_back({ 0, data.size(), tagId + '/' + QString::number(rand_value<uint32>()) });
|
if (_tagMimeProcessor) {
|
||||||
c.insertText(data + ' ');
|
tagId = _tagMimeProcessor->mimeTagFromTag(tagId);
|
||||||
|
}
|
||||||
|
_insertedTags.push_back({ 0, text.size(), tagId });
|
||||||
|
cursor.insertText(text + ' ');
|
||||||
_insertedTags.clear();
|
_insertedTags.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlatTextarea::setTagMimeProcessor(std_::unique_ptr<TagMimeProcessor> &&processor) {
|
||||||
|
_tagMimeProcessor = std_::move(processor);
|
||||||
|
}
|
||||||
|
|
||||||
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
|
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
|
||||||
int32 end = textCursor().position(), start = end - 1;
|
int32 end = textCursor().position(), start = end - 1;
|
||||||
if (textCursor().anchor() != end) return;
|
if (textCursor().anchor() != end) return;
|
||||||
|
@ -544,25 +554,38 @@ public:
|
||||||
return _changed;
|
return _changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void feed(const QString &tagId, int currentPosition) {
|
void feed(const QString &randomTagId, int currentPosition) {
|
||||||
if (tagId == _currentTagId) return;
|
if (randomTagId == _currentTagId) return;
|
||||||
|
|
||||||
if (!_currentTagId.isEmpty()) {
|
if (!_currentTagId.isEmpty()) {
|
||||||
FlatTextarea::Tag tag = {
|
int randomPartPosition = _currentTagId.lastIndexOf('/');
|
||||||
_currentStart,
|
t_assert(randomPartPosition > 0);
|
||||||
currentPosition - _currentStart,
|
|
||||||
_currentTagId,
|
bool tagChanged = true;
|
||||||
};
|
if (_currentTag < _tags->size()) {
|
||||||
if (_currentTag >= _tags->size()) {
|
auto &alreadyTag = _tags->at(_currentTag);
|
||||||
|
if (alreadyTag.offset == _currentStart &&
|
||||||
|
alreadyTag.length == currentPosition - _currentStart &&
|
||||||
|
alreadyTag.id == _currentTagId.midRef(0, randomPartPosition)) {
|
||||||
|
tagChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tagChanged) {
|
||||||
_changed = true;
|
_changed = true;
|
||||||
_tags->push_back(tag);
|
FlatTextarea::Tag tag = {
|
||||||
} else if (_tags->at(_currentTag) != tag) {
|
_currentStart,
|
||||||
_changed = true;
|
currentPosition - _currentStart,
|
||||||
(*_tags)[_currentTag] = tag;
|
_currentTagId.mid(0, randomPartPosition),
|
||||||
|
};
|
||||||
|
if (_currentTag < _tags->size()) {
|
||||||
|
(*_tags)[_currentTag] = tag;
|
||||||
|
} else {
|
||||||
|
_tags->push_back(tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++_currentTag;
|
++_currentTag;
|
||||||
}
|
}
|
||||||
_currentTagId = tagId;
|
_currentTagId = randomTagId;
|
||||||
_currentStart = currentPosition;
|
_currentStart = currentPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -771,7 +794,7 @@ void FlatTextarea::parseLinks() { // some code is duplicated in text.cpp!
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newLinks.push_back(qMakePair(domainOffset - 1, p - start - domainOffset + 2));
|
newLinks.push_back({ domainOffset - 1, p - start - domainOffset + 2 });
|
||||||
offset = matchOffset = p - start;
|
offset = matchOffset = p - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,8 +808,8 @@ QStringList FlatTextarea::linksList() const {
|
||||||
QStringList result;
|
QStringList result;
|
||||||
if (!_links.isEmpty()) {
|
if (!_links.isEmpty()) {
|
||||||
QString text(toPlainText());
|
QString text(toPlainText());
|
||||||
for (LinkRanges::const_iterator i = _links.cbegin(), e = _links.cend(); i != e; ++i) {
|
for_const (auto &link, _links) {
|
||||||
result.push_back(text.mid(i->first + 1, i->second - 2));
|
result.push_back(text.mid(link.start + 1, link.length - 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -865,7 +888,7 @@ void removeTags(QTextDocument *document, int from, int end) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the position of the first inserted tag or "changedEnd" value if none found.
|
// Returns the position of the first inserted tag or "changedEnd" value if none found.
|
||||||
int processInsertedTags(QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags) {
|
int processInsertedTags(QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags, FlatTextarea::TagMimeProcessor *processor) {
|
||||||
int firstTagStart = changedEnd;
|
int firstTagStart = changedEnd;
|
||||||
int applyNoTagFrom = changedEnd;
|
int applyNoTagFrom = changedEnd;
|
||||||
for_const (auto &tag, tags) {
|
for_const (auto &tag, tags) {
|
||||||
|
@ -873,7 +896,8 @@ int processInsertedTags(QTextDocument *document, int changedPosition, int change
|
||||||
int tagTo = tagFrom + tag.length;
|
int tagTo = tagFrom + tag.length;
|
||||||
accumulate_max(tagFrom, changedPosition);
|
accumulate_max(tagFrom, changedPosition);
|
||||||
accumulate_min(tagTo, changedEnd);
|
accumulate_min(tagTo, changedEnd);
|
||||||
if (tagTo > tagFrom) {
|
auto tagId = processor ? processor->tagFromMimeTag(tag.id) : tag.id;
|
||||||
|
if (tagTo > tagFrom && !tagId.isEmpty()) {
|
||||||
accumulate_min(firstTagStart, tagFrom);
|
accumulate_min(firstTagStart, tagFrom);
|
||||||
|
|
||||||
prepareFormattingOptimization(document);
|
prepareFormattingOptimization(document);
|
||||||
|
@ -886,7 +910,7 @@ int processInsertedTags(QTextDocument *document, int changedPosition, int change
|
||||||
|
|
||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
format.setAnchor(true);
|
format.setAnchor(true);
|
||||||
format.setAnchorName(tag.id);
|
format.setAnchorName(tagId + '/' + QString::number(rand_value<uint32>()));
|
||||||
format.setForeground(st::defaultTextStyle.linkFg);
|
format.setForeground(st::defaultTextStyle.linkFg);
|
||||||
c.mergeCharFormat(format);
|
c.mergeCharFormat(format);
|
||||||
|
|
||||||
|
@ -945,7 +969,7 @@ struct FormattingAction {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void FlatTextarea::processFormatting(int changedPosition, int changedEnd) {
|
void FlatTextarea::processFormatting(int insertPosition, int insertEnd) {
|
||||||
// Tilde formatting.
|
// Tilde formatting.
|
||||||
auto regularFont = qsl("Open Sans"), semiboldFont = qsl("Open Sans Semibold");
|
auto regularFont = qsl("Open Sans"), semiboldFont = qsl("Open Sans Semibold");
|
||||||
bool tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == regularFont);
|
bool tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == regularFont);
|
||||||
|
@ -958,13 +982,13 @@ void FlatTextarea::processFormatting(int changedPosition, int changedEnd) {
|
||||||
auto doc = document();
|
auto doc = document();
|
||||||
|
|
||||||
// Apply inserted tags.
|
// Apply inserted tags.
|
||||||
int breakTagOnNotLetterTill = processInsertedTags(doc, changedPosition, changedEnd, _insertedTags);
|
int breakTagOnNotLetterTill = processInsertedTags(doc, insertPosition, insertEnd, _insertedTags, _tagMimeProcessor.get());
|
||||||
using ActionType = FormattingAction::Type;
|
using ActionType = FormattingAction::Type;
|
||||||
while (true) {
|
while (true) {
|
||||||
FormattingAction action;
|
FormattingAction action;
|
||||||
|
|
||||||
auto fromBlock = doc->findBlock(changedPosition);
|
auto fromBlock = doc->findBlock(insertPosition);
|
||||||
auto tillBlock = doc->findBlock(changedEnd);
|
auto tillBlock = doc->findBlock(insertEnd);
|
||||||
if (tillBlock.isValid()) tillBlock = tillBlock.next();
|
if (tillBlock.isValid()) tillBlock = tillBlock.next();
|
||||||
|
|
||||||
for (auto block = fromBlock; block != tillBlock; block = block.next()) {
|
for (auto block = fromBlock; block != tillBlock; block = block.next()) {
|
||||||
|
@ -973,11 +997,11 @@ void FlatTextarea::processFormatting(int changedPosition, int changedEnd) {
|
||||||
t_assert(fragment.isValid());
|
t_assert(fragment.isValid());
|
||||||
|
|
||||||
int fragmentPosition = fragment.position();
|
int fragmentPosition = fragment.position();
|
||||||
if (changedPosition >= fragmentPosition + fragment.length()) {
|
if (insertPosition >= fragmentPosition + fragment.length()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int changedPositionInFragment = changedPosition - fragmentPosition; // Can be negative.
|
int changedPositionInFragment = insertPosition - fragmentPosition; // Can be negative.
|
||||||
int changedEndInFragment = changedEnd - fragmentPosition;
|
int changedEndInFragment = insertEnd - fragmentPosition;
|
||||||
if (changedEndInFragment <= 0) {
|
if (changedEndInFragment <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -995,7 +1019,7 @@ void FlatTextarea::processFormatting(int changedPosition, int changedEnd) {
|
||||||
startTagFound = true;
|
startTagFound = true;
|
||||||
auto tagName = charFormat.anchorName();
|
auto tagName = charFormat.anchorName();
|
||||||
if (!tagName.isEmpty()) {
|
if (!tagName.isEmpty()) {
|
||||||
breakTagOnNotLetter = wasInsertTillTheEndOfTag(block, fragmentIt, changedEnd);
|
breakTagOnNotLetter = wasInsertTillTheEndOfTag(block, fragmentIt, insertEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,7 +1082,7 @@ void FlatTextarea::processFormatting(int changedPosition, int changedEnd) {
|
||||||
c.setPosition(action.intervalEnd, QTextCursor::KeepAnchor);
|
c.setPosition(action.intervalEnd, QTextCursor::KeepAnchor);
|
||||||
if (action.type == ActionType::InsertEmoji) {
|
if (action.type == ActionType::InsertEmoji) {
|
||||||
insertEmoji(action.emoji, c);
|
insertEmoji(action.emoji, c);
|
||||||
changedPosition = action.intervalStart + 1;
|
insertPosition = action.intervalStart + 1;
|
||||||
} else if (action.type == ActionType::RemoveTag) {
|
} else if (action.type == ActionType::RemoveTag) {
|
||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
format.setAnchor(false);
|
format.setAnchor(false);
|
||||||
|
@ -1069,7 +1093,7 @@ void FlatTextarea::processFormatting(int changedPosition, int changedEnd) {
|
||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
format.setFontFamily(action.isTilde ? semiboldFont : regularFont);
|
format.setFontFamily(action.isTilde ? semiboldFont : regularFont);
|
||||||
c.mergeCharFormat(format);
|
c.mergeCharFormat(format);
|
||||||
changedPosition = action.intervalEnd;
|
insertPosition = action.intervalEnd;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -1083,6 +1107,9 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
|
||||||
int insertPosition = (_realInsertPosition >= 0) ? _realInsertPosition : position;
|
int insertPosition = (_realInsertPosition >= 0) ? _realInsertPosition : position;
|
||||||
int insertLength = (_realInsertPosition >= 0) ? _realCharsAdded : charsAdded;
|
int insertLength = (_realInsertPosition >= 0) ? _realCharsAdded : charsAdded;
|
||||||
|
|
||||||
|
int removePosition = position;
|
||||||
|
int removeLength = charsRemoved;
|
||||||
|
|
||||||
QTextCursor(document()->docHandle(), 0).joinPreviousEditBlock();
|
QTextCursor(document()->docHandle(), 0).joinPreviousEditBlock();
|
||||||
|
|
||||||
_correcting = true;
|
_correcting = true;
|
||||||
|
@ -1109,20 +1136,24 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
|
||||||
}
|
}
|
||||||
_correcting = false;
|
_correcting = false;
|
||||||
|
|
||||||
if (!_links.isEmpty()) {
|
if (insertPosition == removePosition) {
|
||||||
bool changed = false;
|
if (!_links.isEmpty()) {
|
||||||
for (auto i = _links.begin(); i != _links.end();) {
|
bool changed = false;
|
||||||
if (i->first + i->second <= insertPosition) {
|
for (auto i = _links.begin(); i != _links.end();) {
|
||||||
++i;
|
if (i->start + i->length <= insertPosition) {
|
||||||
} else if (i->first >= insertPosition + charsRemoved) {
|
++i;
|
||||||
i->first += insertLength - charsRemoved;
|
} else if (i->start >= removePosition + removeLength) {
|
||||||
++i;
|
i->start += insertLength - removeLength;
|
||||||
} else {
|
++i;
|
||||||
i = _links.erase(i);
|
} else {
|
||||||
changed = true;
|
i = _links.erase(i);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (changed) emit linksChanged();
|
||||||
}
|
}
|
||||||
if (changed) emit linksChanged();
|
} else {
|
||||||
|
parseLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document()->availableRedoSteps() > 0) {
|
if (document()->availableRedoSteps() > 0) {
|
||||||
|
@ -1223,7 +1254,12 @@ QMimeData *FlatTextarea::createMimeDataFromSelection() const {
|
||||||
TagList tags;
|
TagList tags;
|
||||||
result->setText(getText(start, end, &tags, nullptr));
|
result->setText(getText(start, end, &tags, nullptr));
|
||||||
if (!tags.isEmpty()) {
|
if (!tags.isEmpty()) {
|
||||||
result->setData(qsl("application/x-td-field-tags"), serializeTagsList(tags));
|
if (_tagMimeProcessor) {
|
||||||
|
for (auto &tag : tags) {
|
||||||
|
tag.id = _tagMimeProcessor->mimeTagFromTag(tag.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result->setData(str_const_toString(TagsMimeType), serializeTagsList(tags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -92,7 +92,16 @@ public:
|
||||||
const TagList &getLastTags() const {
|
const TagList &getLastTags() const {
|
||||||
return _oldtags;
|
return _oldtags;
|
||||||
}
|
}
|
||||||
void insertMentionHashtagOrBotCommand(const QString &data, const QString &tagId = QString());
|
void insertTag(const QString &text, QString tagId = QString());
|
||||||
|
|
||||||
|
// If you need to make some preparations of tags before putting them to QMimeData
|
||||||
|
// (and then to clipboard or to drag-n-drop object), here is a strategy for that.
|
||||||
|
class TagMimeProcessor {
|
||||||
|
public:
|
||||||
|
virtual QString mimeTagFromTag(const QString &tagId) = 0;
|
||||||
|
virtual QString tagFromMimeTag(const QString &mimeTag) = 0;
|
||||||
|
};
|
||||||
|
void setTagMimeProcessor(std_::unique_ptr<TagMimeProcessor> &&processor);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
@ -177,6 +186,8 @@ private:
|
||||||
int _realInsertPosition = -1;
|
int _realInsertPosition = -1;
|
||||||
int _realCharsAdded = 0;
|
int _realCharsAdded = 0;
|
||||||
|
|
||||||
|
std_::unique_ptr<TagMimeProcessor> _tagMimeProcessor;
|
||||||
|
|
||||||
style::flatTextarea _st;
|
style::flatTextarea _st;
|
||||||
|
|
||||||
bool _undoAvailable = false;
|
bool _undoAvailable = false;
|
||||||
|
@ -194,8 +205,13 @@ private:
|
||||||
|
|
||||||
bool _correcting = false;
|
bool _correcting = false;
|
||||||
|
|
||||||
typedef QPair<int, int> LinkRange;
|
struct LinkRange {
|
||||||
typedef QList<LinkRange> LinkRanges;
|
int start;
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
friend bool operator==(const LinkRange &a, const LinkRange &b);
|
||||||
|
friend bool operator!=(const LinkRange &a, const LinkRange &b);
|
||||||
|
using LinkRanges = QVector<LinkRange>;
|
||||||
LinkRanges _links;
|
LinkRanges _links;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -205,3 +221,10 @@ inline bool operator==(const FlatTextarea::Tag &a, const FlatTextarea::Tag &b) {
|
||||||
inline bool operator!=(const FlatTextarea::Tag &a, const FlatTextarea::Tag &b) {
|
inline bool operator!=(const FlatTextarea::Tag &a, const FlatTextarea::Tag &b) {
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) {
|
||||||
|
return (a.start == b.start) && (a.length == b.length);
|
||||||
|
}
|
||||||
|
inline bool operator!=(const FlatTextarea::LinkRange &a, const FlatTextarea::LinkRange &b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue