tdesktop/Telegram/SourceFiles/ui/emoji_config.h
John Preston 45143c40c9 FlatTextarea handles tags on insertFromMime and tags editing.
Fixed dependent messages update when message was edited.
Fixed entities parsing in Text when they follow one after another.
2016-05-04 19:46:24 +03:00

241 lines
8.3 KiB
C

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/text/text.h"
void emojiInit();
EmojiPtr emojiGet(uint32 code);
EmojiPtr emojiGet(uint32 code, uint32 code2);
EmojiPtr emojiGet(EmojiPtr emoji, uint32 color);
EmojiPtr emojiGet(const QChar *from, const QChar *end);
QString emojiGetSequence(int index);
inline QString emojiString(EmojiPtr emoji) {
if ((emoji->code & 0xFFFF0000U) == 0xFFFF0000U) { // sequence
return emojiGetSequence(emoji->code & 0xFFFFU);
}
QString result;
result.reserve(emoji->len + (emoji->postfix ? 1 : 0));
if (!(emoji->code >> 16)) {
result.append(QChar(emoji->code & 0xFFFF));
} else {
result.append(QChar((emoji->code >> 16) & 0xFFFF));
result.append(QChar(emoji->code & 0xFFFF));
if (emoji->code2) {
result.append(QChar((emoji->code2 >> 16) & 0xFFFF));
result.append(QChar(emoji->code2 & 0xFFFF));
}
}
if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
result.append(QChar((emoji->color >> 16) & 0xFFFF));
result.append(QChar(emoji->color & 0xFFFF));
}
if (emoji->postfix) result.append(QChar(emoji->postfix));
return result;
}
inline uint64 emojiKey(EmojiPtr emoji) {
uint64 key = emoji->code;
if (emoji->code2) {
key = (key << 32) | uint64(emoji->code2);
} else if (emoji->color && ((emoji->color & 0xFFFF0000U) != 0xFFFF0000U)) {
key = (key << 32) | uint64(emoji->color);
}
return key;
}
inline EmojiPtr emojiFromKey(uint64 key) {
uint32 code = uint32(key >> 32), code2 = uint32(key & 0xFFFFFFFFLLU);
if (!code && code2) {
code = code2;
code2 = 0;
}
EmojiPtr emoji = emojiGet(code);
if (emoji == TwoSymbolEmoji) {
return emojiGet(code, code2);
} else if (emoji && emoji->color && code2) {
return emojiGet(emoji, code2);
}
return emoji;
}
inline EmojiPtr emojiFromUrl(const QString &url) {
return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e.
}
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *end, int *outLength = nullptr) {
EmojiPtr emoji = nullptr;
if (ch + 1 < end && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) {
uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode();
emoji = emojiGet(code);
if (emoji) {
if (emoji == TwoSymbolEmoji) { // check two symbol
if (ch + 3 >= end) {
emoji = 0;
} else {
uint32 code2 = ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode()));
emoji = emojiGet(code, code2);
}
} else {
if (ch + 2 < end && (ch + 2)->unicode() == 0x200D) { // check sequence
EmojiPtr seq = emojiGet(ch, end);
if (seq) {
emoji = seq;
}
}
}
}
} else if (ch + 2 < end && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) {
uint32 code = (ch->unicode() << 16) | (ch + 2)->unicode();
emoji = emojiGet(code);
if (outLength) *outLength = emoji->len + 1;
return emoji;
} else if (ch < end) {
emoji = emojiGet(ch->unicode());
t_assert(emoji != TwoSymbolEmoji);
}
if (emoji) {
int32 len = emoji->len + ((ch + emoji->len < end && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
if (emoji->color && (ch + len + 1 < end && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode()));
EmojiPtr col = emojiGet(emoji, color);
if (col && col != emoji) {
len += col->len - emoji->len;
emoji = col;
if (ch + len < end && (ch + len)->unicode() == 0xFE0F) {
++len;
}
}
}
if (outLength) *outLength = len;
}
return emoji;
}
inline EmojiPtr emojiFromText(const QString &text, int32 *plen = 0) {
return text.isEmpty() ? EmojiPtr(0) : emojiFromText(text.constBegin(), text.constEnd(), plen);
}
inline EmojiPtr emojiGetNoColor(EmojiPtr emoji) {
if (emoji && emoji->color && (emoji->color & 0xFFFF0000U) != 0xFFFF0000U) {
EmojiPtr result = emojiGet(emoji->code);
return (result == TwoSymbolEmoji) ? emojiGet(emoji->code, emoji->code2) : result;
}
return emoji;
}
extern int EmojiSizes[5], EIndex, ESize;
extern const char *EmojiNames[5], *EName;
void emojiFind(const QChar *ch, const QChar *e, const QChar *&newEmojiEnd, uint32 &emojiCode);
inline bool emojiEdge(const QChar *ch) {
return true;
switch (ch->unicode()) {
case '.': case ',': case ':': case ';': case '!': case '?': case '#': case '@':
case '(': case ')': case '[': case ']': case '{': case '}': case '<': case '>':
case '+': case '=': case '-': case '_': case '*': case '/': case '\\': case '^': case '$':
case '"': case '\'':
case 8212: case 171: case 187: // --, <<, >>
return true;
}
return false;
}
inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText *inOutEntities) {
if (to > from) {
for (auto &entity : *inOutEntities) {
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());
}
if (entity.offset() + entity.length() < to - start) {
entity.shrinkFromRight(from - start - result.size());
}
}
result.append(from, to - from);
}
}
inline QString replaceEmojis(const QString &text, EntitiesInText *inOutEntities) {
QString result;
auto currentEntity = inOutEntities->begin(), entitiesEnd = inOutEntities->end();
const QChar *emojiStart = text.constData(), *emojiEnd = emojiStart, *e = text.constData() + text.size();
bool canFindEmoji = true;
for (const QChar *ch = emojiEnd; ch != e;) {
uint32 emojiCode = 0;
const QChar *newEmojiEnd = 0;
if (canFindEmoji) {
emojiFind(ch, e, newEmojiEnd, emojiCode);
}
while (currentEntity != entitiesEnd && ch >= emojiStart + currentEntity->offset() + currentEntity->length()) {
++currentEntity;
}
EmojiPtr emoji = emojiCode ? emojiGet(emojiCode) : 0;
if (emoji && emoji != TwoSymbolEmoji &&
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
(newEmojiEnd == e || !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());
appendPartToResult(result, emojiStart, emojiEnd, ch, inOutEntities);
if (emoji->color) {
EmojiColorVariants::const_iterator it = cEmojiVariants().constFind(emoji->code);
if (it != cEmojiVariants().cend()) {
EmojiPtr replace = emojiFromKey(it.value());
if (replace) {
if (replace != TwoSymbolEmoji && replace->code == emoji->code && replace->code2 == emoji->code2) {
emoji = replace;
}
}
}
}
result.append(emojiString(emoji));
ch = emojiEnd = newEmojiEnd;
canFindEmoji = true;
} else {
if (emojiEdge(ch)) {
canFindEmoji = true;
} else {
canFindEmoji = false;
}
++ch;
}
}
if (result.isEmpty()) return text;
appendPartToResult(result, emojiStart, emojiEnd, e, inOutEntities);
return result;
}
int emojiPackCount(DBIEmojiTab tab);
EmojiPack emojiPack(DBIEmojiTab tab);