void FlatTextarea::getMentionHashtagStart(QString &start) const { int32 pos = textCursor().position(); if (textCursor().anchor() != pos) return; QTextDocument *doc(document()); QTextBlock block = doc->findBlock(pos); for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) { QTextFragment fr(iter.fragment()); if (!fr.isValid()) continue; int32 p = fr.position(), e = (p + fr.length()); if (p >= pos || e < pos) continue; QTextCharFormat f = fr.charFormat(); if (f.isImageFormat()) continue; QString t(fr.text()); for (int i = pos - p; i > 0; --i) { if (t.at(i - 1) == '@') { if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) { start = t.mid(i - 1, pos - p - i + 1); } return; } else if (t.at(i - 1) == '#') { if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) { start = t.mid(i - 1, pos - p - i + 1); } return; } if (pos - p - i > 63) break; if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; } return; } }
void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { int32 emojiPosition = 0, emojiLen = 0; const EmojiData *emoji = 0; QTextDocument *doc(document()); while (true) { int32 start = position, end = position + charsAdded; QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); if (till.isValid()) till = till.next(); for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 fp = fragment.position(), fe = fp + fragment.length(); if (fp >= end || fe <= start) { continue; } QString t(fragment.text()); const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch) { emoji = emojiFromText(ch, e, emojiLen); if (emoji) { emojiPosition = fp + (ch - t.constData()); break; } if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch; } if (emoji) break; } if (emoji) break; } if (emoji) { if (!document()->pageSize().isNull()) { document()->setPageSize(QSizeF(0, 0)); } QTextCursor c(doc->docHandle(), emojiPosition); c.setPosition(emojiPosition + emojiLen, QTextCursor::KeepAnchor); int32 removedUpto = c.position(); insertEmoji(emoji, c); charsAdded -= removedUpto - position; position = emojiPosition + 1; emoji = 0; emojiPosition = 0; } else { break; } } }
QString Text::extractImgTooltip(int pos) const { for (QTextBlock::Iterator itr = doc->firstBlock().begin(); itr != doc->firstBlock().end(); ++itr) { if (itr.fragment().contains(pos) && itr.fragment().charFormat().isImageFormat()) { QTextImageFormat imgFmt = itr.fragment().charFormat().toImageFormat(); return imgFmt.toolTip(); } } return QString(); }
QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start) const { start = false; int32 pos = textCursor().position(); if (textCursor().anchor() != pos) return QString(); // check mention / hashtag / bot command QTextDocument *doc(document()); QTextBlock block = doc->findBlock(pos); for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) { QTextFragment fr(iter.fragment()); if (!fr.isValid()) continue; int32 p = fr.position(), e = (p + fr.length()); if (p >= pos || e < pos) continue; QTextCharFormat f = fr.charFormat(); if (f.isImageFormat()) continue; bool mentionInCommand = false; QString t(fr.text()); for (int i = pos - p; i > 0; --i) { if (t.at(i - 1) == '@') { if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) { start = (i == 1) && (p == 0); return t.mid(i - 1, pos - p - i + 1); } else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) { mentionInCommand = true; --i; continue; } return QString(); } else if (t.at(i - 1) == '#') { if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) { start = (i == 1) && (p == 0); return t.mid(i - 1, pos - p - i + 1); } return QString(); } else if (t.at(i - 1) == '/') { if (i < 2) { start = (i == 1) && (p == 0); return t.mid(i - 1, pos - p - i + 1); } return QString(); } if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break; if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; } break; } return QString(); }
bool FlatTextarea::hasText() const { QTextDocument *doc(document()); QTextBlock from = doc->begin(), till = doc->end(); if (from == till) return false; for (QTextBlock::Iterator iter = from.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; if (!fragment.text().isEmpty()) return true; } return (from.next() != till); }
void FlatTextarea::onMentionHashtagOrBotCommandInsert(QString str) { QTextCursor c(textCursor()); int32 pos = c.position(); QTextDocument *doc(document()); QTextBlock block = doc->findBlock(pos); for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) { QTextFragment fr(iter.fragment()); if (!fr.isValid()) continue; int32 p = fr.position(), e = (p + fr.length()); if (p >= pos || e < pos) continue; QTextCharFormat f = fr.charFormat(); if (f.isImageFormat()) continue; bool mentionInCommand = false; QString t(fr.text()); for (int i = pos - p; i > 0; --i) { if (t.at(i - 1) == '@' || t.at(i - 1) == '#' || t.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) == '_'))) { c.setPosition(p + i - 1, QTextCursor::MoveAnchor); int till = p + i; for (; (till < e) && (till - p - i + 1 < str.size()); ++till) { if (t.at(till - p).toLower() != str.at(till - p - i + 1).toLower()) { break; } } if (till - p - i + 1 == str.size() && till < e && t.at(till - p) == ' ') { ++till; } c.setPosition(till, QTextCursor::KeepAnchor); c.insertText(str + ' '); return; } 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) { mentionInCommand = true; --i; continue; } break; } if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break; if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; } break; } c.insertText(str + ' '); }
void FlatTextarea::onMentionOrHashtagInsert(QString mentionOrHashtag) { QTextCursor c(textCursor()); int32 pos = c.position(); QTextDocument *doc(document()); QTextBlock block = doc->findBlock(pos); for (QTextBlock::Iterator iter = block.begin(); !iter.atEnd(); ++iter) { QTextFragment fr(iter.fragment()); if (!fr.isValid()) continue; int32 p = fr.position(), e = (p + fr.length()); if (p >= pos || e < pos) continue; QTextCharFormat f = fr.charFormat(); if (f.isImageFormat()) continue; QString t(fr.text()); for (int i = pos - p; i > 0; --i) { if (t.at(i - 1) == '@' || t.at(i - 1) == '#') { if ((i == pos - p || t.at(i).isLetter() || t.at(i - 1) == '#') && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) { c.setPosition(p + i - 1, QTextCursor::MoveAnchor); int till = p + i; for (; (till < e) && (till - p - i + 1 < mentionOrHashtag.size()); ++till) { if (t.at(till - p).toLower() != mentionOrHashtag.at(till - p - i + 1).toLower()) { break; } } if (till - p - i + 1 == mentionOrHashtag.size() && till < e && t.at(till - p) == ' ') { ++till; } c.setPosition(till, QTextCursor::KeepAnchor); c.insertText(mentionOrHashtag + ' '); return; } break; } if (pos - p - i > 63) break; if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break; } } c.insertText(mentionOrHashtag + ' '); }
QString Text::extractSanitizedText(int from, int to) const { if (!doc) return ""; QString txt; QTextBlock begin = doc->findBlock(from); QTextBlock end = doc->findBlock(to); for (QTextBlock block = begin; block != end.next() && block.isValid(); block = block.next()) { for (QTextBlock::Iterator itr = block.begin(); itr != block.end(); ++itr) { int pos = itr.fragment() .position(); // fragment position -> position of the first character in the fragment if (itr.fragment().charFormat().isImageFormat()) { QTextImageFormat imgFmt = itr.fragment().charFormat().toImageFormat(); QString key = imgFmt.name(); // img key (eg. key::D for :D) QString rune = key.mid(4); if (pos >= from && pos < to) { txt += rune; ++pos; } } else { for (QChar c : itr.fragment().text()) { if (pos >= from && pos < to) txt += c; ++pos; } } } txt += '\n'; } txt.chop(1); return txt; }
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const { int32 end = textCursor().position(), start = end - 1; if (textCursor().anchor() != end) return; if (start < 0) start = 0; QTextDocument *doc(document()); QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); if (till.isValid()) till = till.next(); for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fr(iter.fragment()); if (!fr.isValid()) continue; int32 p = fr.position(), e = (p + fr.length()); if (p >= end || e <= start) { continue; } QTextCharFormat f = fr.charFormat(); QString t(fr.text()); if (p < start) { t = t.mid(start - p, end - start); } else if (e > end) { t = t.mid(0, end - p); } if (f.isImageFormat() && !t.isEmpty() && t.at(0).unicode() == QChar::ObjectReplacementCharacter) { QString imageName = static_cast<QTextImageFormat*>(&f)->name(); if (imageName.startsWith(QLatin1String("emoji://e."))) { fragment = fr; text = t; return; } } return; } } return; }
QString FlatTextarea::getText(int32 start, int32 end) const { if (end >= 0 && end <= start) return QString(); if (start < 0) start = 0; bool full = (start == 0) && (end < 0); QTextDocument *doc(document()); QTextBlock from = full ? doc->begin() : doc->findBlock(start), till = (end < 0) ? doc->end() : doc->findBlock(end); if (till.isValid()) till = till.next(); int32 possibleLen = 0; for (QTextBlock b = from; b != till; b = b.next()) { possibleLen += b.length(); } QString result; result.reserve(possibleLen + 1); if (!full && end < 0) { end = possibleLen; } for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 p = full ? 0 : fragment.position(), e = full ? 0 : (p + fragment.length()); if (!full) { if (p >= end || e <= start) { continue; } } QTextCharFormat f = fragment.charFormat(); QString emojiText; QString t(fragment.text()); if (!full) { if (p < start) { t = t.mid(start - p, end - start); } else if (e > end) { t = t.mid(0, end - p); } } QChar *ub = t.data(), *uc = ub, *ue = uc + t.size(); for (; uc != ue; ++uc) { switch (uc->unicode()) { case 0xfdd0: // QTextBeginningOfFrame case 0xfdd1: // QTextEndOfFrame case QChar::ParagraphSeparator: case QChar::LineSeparator: *uc = QLatin1Char('\n'); break; case QChar::Nbsp: *uc = QLatin1Char(' '); break; case QChar::ObjectReplacementCharacter: if (emojiText.isEmpty() && f.isImageFormat()) { QString imageName = static_cast<QTextImageFormat*>(&f)->name(); if (imageName.startsWith(QLatin1String("emoji://e."))) { if (EmojiPtr emoji = emojiFromUrl(imageName)) { emojiText = textEmojiString(emoji); } } } if (uc > ub) result.append(ub, uc - ub); if (!emojiText.isEmpty()) result.append(emojiText); ub = uc + 1; break; } } if (uc > ub) result.append(ub, uc - ub); } result.append('\n'); } result.chop(1); return result; }
void InputField::processDocumentContentsChange(int position, int charsAdded) { int32 emojiPosition = 0, emojiLen = 0; const EmojiData *emoji = 0; static QString space(' '); QTextDocument *doc(_inner.document()); QTextCursor c(_inner.textCursor()); c.joinPreviousEditBlock(); while (true) { int32 start = position, end = position + charsAdded; QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); if (till.isValid()) till = till.next(); for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 fp = fragment.position(), fe = fp + fragment.length(); if (fp >= end || fe <= start) { continue; } QString t(fragment.text()); const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch) { // QTextBeginningOfFrame // QTextEndOfFrame if (ch->unicode() == 0xfdd0 || ch->unicode() == 0xfdd1 || ch->unicode() == QChar::ParagraphSeparator || ch->unicode() == QChar::LineSeparator || ch->unicode() == '\n' || ch->unicode() == '\r') { if (!_inner.document()->pageSize().isNull()) { _inner.document()->setPageSize(QSizeF(0, 0)); } int32 nlPosition = fp + (ch - t.constData()); QTextCursor c(doc->docHandle(), nlPosition); c.setPosition(nlPosition + 1, QTextCursor::KeepAnchor); c.insertText(space); position = nlPosition + 1; emoji = TwoSymbolEmoji; // just a flag break; } emoji = emojiFromText(ch, e, emojiLen); if (emoji) { emojiPosition = fp + (ch - t.constData()); break; } if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) ++ch; } if (emoji) break; } if (emoji) break; if (b.next() != doc->end()) { int32 nlPosition = b.next().position() - 1; QTextCursor c(doc->docHandle(), nlPosition); c.setPosition(nlPosition + 1, QTextCursor::KeepAnchor); c.insertText(space); position = nlPosition + 1; emoji = TwoSymbolEmoji; // just a flag break; } } if (emoji == TwoSymbolEmoji) { // just skip emoji = 0; emojiPosition = 0; } else if (emoji) { if (!_inner.document()->pageSize().isNull()) { _inner.document()->setPageSize(QSizeF(0, 0)); } QTextCursor c(doc->docHandle(), emojiPosition); c.setPosition(emojiPosition + emojiLen, QTextCursor::KeepAnchor); int32 removedUpto = c.position(); insertEmoji(emoji, c); for (Insertions::iterator i = _insertions.begin(), e = _insertions.end(); i != e; ++i) { if (i->first >= removedUpto) { i->first -= removedUpto - emojiPosition - 1; } else if (i->first >= emojiPosition) { i->second -= removedUpto - emojiPosition; i->first = emojiPosition + 1; } else if (i->first + i->second > emojiPosition + 1) { i->second -= qMin(removedUpto, i->first + i->second) - emojiPosition; } } charsAdded -= removedUpto - position; position = emojiPosition + 1; emoji = 0; emojiPosition = 0; } else { break; } } c.endEditBlock(); }
void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { int32 replacePosition = -1, replaceLen = 0; const EmojiData *emoji = 0; static QString regular = qsl("Open Sans"), semibold = qsl("Open Sans Semibold"); bool checkTilde = !cRetina() && (font().pixelSize() == 13) && (font().family() == regular), wasTildeFragment = false; QTextDocument *doc(document()); while (true) { int32 start = position, end = position + charsAdded; QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); if (till.isValid()) till = till.next(); for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 fp = fragment.position(), fe = fp + fragment.length(); if (fp >= end || fe <= start) { continue; } if (checkTilde) { wasTildeFragment = (fragment.charFormat().fontFamily() == semibold); } QString t(fragment.text()); const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch, ++fp) { int32 emojiLen = 0; emoji = emojiFromText(ch, e, &emojiLen); if (emoji) { if (replacePosition >= 0) { emoji = 0; // replace tilde char format first } else { replacePosition = fp; replaceLen = emojiLen; } break; } if (checkTilde && fp >= position) { // tilde fix in OpenSans bool tilde = (ch->unicode() == '~'); if ((tilde && !wasTildeFragment) || (!tilde && wasTildeFragment)) { if (replacePosition < 0) { replacePosition = fp; replaceLen = 1; } else { ++replaceLen; } } else if (replacePosition >= 0) { break; } } if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) { ++ch; ++fp; } } if (replacePosition >= 0) break; } if (replacePosition >= 0) break; } if (replacePosition >= 0) { if (!document()->pageSize().isNull()) { document()->setPageSize(QSizeF(0, 0)); } QTextCursor c(doc->docHandle(), replacePosition); c.setPosition(replacePosition + replaceLen, QTextCursor::KeepAnchor); if (emoji) { insertEmoji(emoji, c); } else { QTextCharFormat format; format.setFontFamily(wasTildeFragment ? regular : semibold); c.mergeCharFormat(format); } charsAdded -= replacePosition + replaceLen - position; position = replacePosition + (emoji ? 1 : replaceLen); emoji = 0; replacePosition = -1; } else { break; } } }
void HtmlExporter::emitBlock( const QTextBlock &block ) { // save and later restore, in case we 'change' the default format by // emitting block char format information // NOTE the bottom line is commented, to use default charFormat, which can be set from outside. //QTextCharFormat oldDefaultCharFormat = defaultCharFormat; QString blockTag; bool isBlockQuote = false; const QTextBlockFormat blockFormat = block.blockFormat(); if ( blockFormat.hasProperty( BilboTextFormat::IsBlockQuote ) && blockFormat.boolProperty( BilboTextFormat::IsBlockQuote ) ) { isBlockQuote = true; } QTextList *list = block.textList(); if ( list ) { if ( list->itemNumber( block ) == 0 ) { // first item? emit <ul> or appropriate // qDebug() << "first item" << endl; if ( isBlockQuote ) { html += QLatin1String( "<blockquote>" ); } const QTextListFormat format = list->format(); const int style = format.style(); switch ( style ) { case QTextListFormat::ListDecimal: html += QLatin1String( "<ol" ); break; case QTextListFormat::ListDisc: html += QLatin1String( "<ul" ); break; case QTextListFormat::ListCircle: html += QLatin1String( "<ul type=\"circle\"" ); break; case QTextListFormat::ListSquare: html += QLatin1String( "<ul type=\"square\"" ); break; case QTextListFormat::ListLowerAlpha: html += QLatin1String( "<ol type=\"a\"" ); break; case QTextListFormat::ListUpperAlpha: html += QLatin1String( "<ol type=\"A\"" ); break; default: html += QLatin1String( "<ul" ); // ### should not happen //qDebug() << html; } /* if (format.hasProperty(QTextFormat::ListIndent)) { html += QLatin1String(" style=\"-qt-list-indent: "); html += QString::number(format.indent()); html += QLatin1String(";\""); }*/ html += QLatin1Char( '>' ); } blockTag = QLatin1String( "li" ); // html += QLatin1String( "<li " ); } // const QTextBlockFormat blockFormat = block.blockFormat(); if ( blockFormat.hasProperty( QTextFormat::BlockTrailingHorizontalRulerWidth ) ) { if ( ( blockFormat.hasProperty( BilboTextFormat::IsHtmlTagSign ) ) && ( blockFormat.boolProperty( BilboTextFormat::IsHtmlTagSign ) ) ) { html += QLatin1String( "<!--split-->" ); return; } else { html += QLatin1String( "<hr" ); QTextLength width = blockFormat.lengthProperty( QTextFormat::BlockTrailingHorizontalRulerWidth ); if ( width.type() != QTextLength::VariableLength ) { emitTextLength( "width", width ); } else { html += QLatin1Char( ' ' ); } html += QLatin1String( "/>" ); return; } } const bool pre = blockFormat.nonBreakableLines(); if ( pre ) { // qDebug() << "NonBreakable lines" << endl; // if (list) { // html += QLatin1Char('>'); // } // html += QLatin1String( "<pre" ); // emitBlockAttributes( block ); // html += QLatin1Char( '>' ); blockTag = QLatin1String( "pre" ); } else { if (!list) { if ( isBlockQuote ) { html += QLatin1String( "<blockquote>" ); } if ( ( blockFormat.hasProperty( BilboTextFormat::HtmlHeading ) ) && ( blockFormat.intProperty( BilboTextFormat::HtmlHeading ) ) ) { const int index = blockFormat.intProperty( BilboTextFormat::HtmlHeading ); blockTag = QLatin1Char( 'h' ) + QString::number( index ); } else { //html += QLatin1String("<div"); // html += QLatin1String( "<p" ); blockTag = QLatin1String( "p" ); } } } if ( !blockTag.isEmpty() ) { html += QLatin1Char( '<' ) + blockTag; emitBlockAttributes( block ); html += QLatin1Char( '>' ); } QTextBlock::Iterator it = block.begin(); for ( ; !it.atEnd(); ++it ) { emitFragment( it.fragment(), blockFormat ); } if ( !blockTag.isEmpty() ) { html += QLatin1String( "</" ) + blockTag + QLatin1String( ">\n" ); } // if ( pre ) { // html += QLatin1String( "</pre>\n" ); // } else { // if ( list ) { // html += QLatin1String( "</li>\n" ); // } else { // if ( blockFormat::boolProperty( BilboTextFormat::IsHtmlHeading ) ) { // const int index = format.intProperty( QTextFormat::FontSizeAdjustment ); // switch ( index ) { // case -2: // html += QLatin1String( "</h6>" ); // break; // case -1: // html += QLatin1String( "</h5>" ); // break; // case 0: // html += QLatin1String( "</h4>" ); // break; // case 1: // html += QLatin1String( "</h3>" ); // break; // case 2: // html += QLatin1String( "<h2" ); // break; // case 3: // html += QLatin1String( "<h1" ); // break; // } // } else { // html += QLatin1String( "</p>\n" ); // } // } // } // HACK html.replace( QRegExp("<br[\\s]*/>[\\n]*<br[\\s]*/>[\\n]*"),"<br /> <br />" ); if ( list ) { if ( list->itemNumber( block ) == list->count() - 1 ) { // last item? close list if ( isOrderedList( list->format().style() ) ) { html += QLatin1String( "</ol>\n" ); } else { html += QLatin1String( "</ul>\n" ); } if ( isBlockQuote ) { html += QLatin1String( "</blockquote>\n" ); } } } else { if ( isBlockQuote ) { html += QLatin1String( "</blockquote>\n" ); } } // NOTE the bottom line is commented, to use default charFormat, which can be set from outside. //defaultCharFormat = oldDefaultCharFormat; }
void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) { int32 emojiPosition = 0, emojiLen = 0; const EmojiData *emoji = 0; QTextDocument *doc(document()); while (true) { int32 start = position, end = position + charsAdded; QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); if (till.isValid()) till = till.next(); for (QTextBlock b = from; b != till; b = b.next()) { for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) { QTextFragment fragment(iter.fragment()); if (!fragment.isValid()) continue; int32 p = fragment.position(), e = p + fragment.length(); if (p >= end || e <= start) { continue; } QString t(fragment.text()); for (const QChar *ch = t.constData(), *e = ch + t.size(); ch != e; ++ch) { if (ch + 1 < e && (ch->isHighSurrogate() || (((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3))) { emoji = getEmoji((ch->unicode() << 16) | (ch + 1)->unicode()); if (emoji) { if (emoji->len == 4 && (ch + 3 >= e || ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode())) != emoji->code2)) { emoji = 0; } else { emojiPosition = p + (ch - t.constData()); emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0); break; } } ++ch; } else { emoji = getEmoji(ch->unicode()); if (emoji) { emojiPosition = p + (ch - t.constData()); emojiLen = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0); break; } } } if (emoji) break; } if (emoji) break; } if (emoji) { QTextCursor c(doc->docHandle(), emojiPosition); c.setPosition(emojiPosition + emojiLen, QTextCursor::KeepAnchor); int32 removedUpto = c.position(); insertEmoji(emoji, c); for (Insertions::iterator i = _insertions.begin(), e = _insertions.end(); i != e; ++i) { if (i->first >= removedUpto) { i->first -= removedUpto - emojiPosition - 1; } else if (i->first >= emojiPosition) { i->second -= removedUpto - emojiPosition; i->first = emojiPosition + 1; } else if (i->first + i->second > emojiPosition + 1) { i->second -= qMin(removedUpto, i->first + i->second) - emojiPosition; } } charsAdded -= removedUpto - position; position = emojiPosition + 1; emoji = 0; emojiPosition = 0; } else { break; } } }