24 #include <QDomElement>
25 #include <QRegularExpression>
26 #include <QStandardPaths>
27 #include <QStringBuilder>
28 #include <QtConcurrent/QtConcurrentRun>
31 #if defined(Q_OS_FREEBSD)
59 static const QString RICH_TEXT_PATTERN = QStringLiteral(
"<img title=\"%1\" src=\"key:%1\"\\>");
61 static const QString EMOTICONS_FILE_NAME = QStringLiteral(
"emoticons.xml");
63 static constexpr
int CLEANUP_TIMEOUT = 5 * 60 * 1000;
72 #if defined(Q_OS_FREEBSD)
75 setlocale(LC_ALL,
"");
77 const QString EMOTICONS_SUB_PATH = QDir::separator() + QStringLiteral(
"emoticons");
78 QStringList paths{
":/smileys",
"~/.kde4/share/emoticons",
"~/.kde/share/emoticons",
82 QStandardPaths::StandardLocation location;
83 location = QStandardPaths::AppDataLocation;
85 QStringList locations = QStandardPaths::standardLocations(location);
87 locations.append(QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation));
88 for (QString qtoxPath : locations) {
89 qtoxPath.append(EMOTICONS_SUB_PATH);
90 if (!paths.contains(qtoxPath)) {
91 paths.append(qtoxPath);
105 return RICH_TEXT_PATTERN.arg(key);
110 constexpr
auto asciiExtMask = 0x80;
112 return (
string.toUtf8()[0] & asciiExtMask) == 0;
116 : cleanupTimer{
new QTimer(
this)}
123 cleanupTimer->start(CLEANUP_TIMEOUT);
135 std::shared_ptr<QIcon>& icon = it->second;
136 if (icon.use_count() == 1) {
169 const QString homePath = QDir::homePath();
170 for (QString
path : paths) {
171 if (
path.startsWith(
'~')) {
172 path.replace(0, 1, homePath);
180 for (
const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
181 dir.cd(subdirectory);
182 if (dir.exists(EMOTICONS_FILE_NAME)) {
183 QString absPath = dir.absolutePath() + QDir::separator() + EMOTICONS_FILE_NAME;
184 QPair<QString, QString> p{dir.dirName(), absPath};
185 if (!smileyPacks.contains(p)) {
186 smileyPacks.append(p);
205 QFile xmlFile(filename);
206 if (!xmlFile.exists() || !xmlFile.open(QIODevice::ReadOnly)) {
212 doc.setContent(xmlFile.readAll());
230 path = QFileInfo(filename).absolutePath();
231 QDomNodeList emoticonElements = doc.elementsByTagName(
"emoticon");
232 const QString itemName = QStringLiteral(
"file");
233 const QString childName = QStringLiteral(
"string");
234 const int iconsCount = emoticonElements.size();
239 for (
int i = 0; i < iconsCount; ++i) {
240 QDomNode node = emoticonElements.at(i);
241 QString iconName = node.attributes().namedItem(itemName).nodeValue();
242 QString iconPath = QDir{
path}.filePath(iconName);
243 QDomElement stringElement = node.firstChildElement(childName);
244 QStringList emoticonList;
245 while (!stringElement.isNull()) {
246 QString emoticon = stringElement.text().replace(
"<",
"<").replace(
">",
">");
248 emoticonList.append(emoticon);
249 stringElement = stringElement.nextSibling().toElement();
266 QString allPattern = QStringLiteral(
"(");
267 QString regularPatterns;
268 QString multiCharacterEmojiPatterns;
273 if (emote.toUcs4().length() == 1) {
274 regularPatterns.append(emote);
275 regularPatterns.append(QStringLiteral(
"|"));
278 multiCharacterEmojiPatterns.append(emote);
279 multiCharacterEmojiPatterns.append(QStringLiteral(
"|"));
283 regularPatterns.append(QStringLiteral(R
"((?<=^|\s))") % QRegularExpression::escape(emote) % QStringLiteral(R"((?=$|\s))"));
284 regularPatterns.append(QStringLiteral("|"));
289 allPattern.append(multiCharacterEmojiPatterns);
290 allPattern.append(regularPatterns);
291 allPattern[allPattern.size() - 1] = QChar(
')');
294 smilify.setPattern(allPattern);
309 QRegularExpressionMatchIterator iter =
smilify.globalMatch(result);
310 while (iter.hasNext()) {
311 QRegularExpressionMatch match = iter.next();
312 int startPos = match.capturedStart();
313 int keyLength = match.capturedLength();
315 result.replace(startPos + replaceDiff, keyLength, imgRichText);
316 replaceDiff += imgRichText.length() - keyLength;
345 return std::make_shared<QIcon>();
348 const QString& iconPath = iconPathIt.value();
349 auto icon = std::make_shared<QIcon>(iconPath);